{"id":724,"date":"2024-02-05T12:07:03","date_gmt":"2024-02-05T18:07:03","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=724"},"modified":"2024-02-05T14:28:45","modified_gmt":"2024-02-05T20:28:45","slug":"basic-dialog-usage-and-gotchas-to-watch-for","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/basic-dialog-usage-and-gotchas-to-watch-for\/","title":{"rendered":"Basic Dialog Usage and Gotchas To Watch For"},"content":{"rendered":"\n<p>The <code>&lt;dialog&gt;<\/code> element in HTML is tremendous. We&#8217;ve got <a href=\"https:\/\/caniuse.com\/dialog\">support across the board<\/a> now, so using it is a smart plan. Just with basic usage, you get a centered modal dialog experience that comes up when you call it, a dimmed background, focus trapped within it, closes with the ESC key, and focus returning where it came from. You can style it all entirely predictably in CSS. Those things range from a little bit of a pain to downright hard to pull off if left to our own implementations. Now we get them all <em>for free<\/em> as they say.<\/p>\n\n\n\n<p>You don&#8217;t automatically get a close button, so here&#8217;s a basic implementation that adds that.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"show-dialog-button\"<\/span>&gt;<\/span>Show Dialog<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dialog<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"dialog\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"dialog-title\"<\/span>&gt;<\/span>\n    Hi, I'm a dialog.\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">aria-label<\/span>=<span class=\"hljs-string\">\"Close Dialog\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"close-dialog-button\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svg<\/span> <span class=\"hljs-attr\">...<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dialog<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now we need two click handlers, one for opening and one for closing.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> showDialogButton = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\".show-dialog-button\"<\/span>);\n<span class=\"hljs-keyword\">const<\/span> closeDialogButton = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\".close-dialog-button\"<\/span>);\n<span class=\"hljs-keyword\">const<\/span> dialog = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\"dialog\"<\/span>);\n\nshowDialogButton.addEventListener(<span class=\"hljs-string\">\"click\"<\/span>, () =&gt; {\n  dialog.showModal();\n});\n\ncloseDialogButton.addEventListener(<span class=\"hljs-string\">\"click\"<\/span>, () =&gt; {\n  dialog.close();\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I think the naming of things here almost qualifies as a gotcha. So it&#8217;s <code>showModal()<\/code> eh? Why not <code>showDialog()<\/code>, since, ya know, it&#8217;s for the <code>&lt;dialog&gt;<\/code> element? So to close it it must be <code>closeDialog()<\/code> or <code>hideDialog()<\/code> surely, right? No. Just <code>close()<\/code>. Oh well I&#8217;m sure that was <a href=\"https:\/\/en.wiktionary.org\/wiki\/bikeshedding\">bikeshedded<\/a> to death and there are probably <em>\u2728 reasons<\/em>.<\/p>\n\n\n\n<p>That code above is it really, that&#8217;s largely functional. And that, friends, is extremely cool. Here&#8217;s a quick live demo:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_VwRxOWg\" src=\"\/\/codepen.io\/anon\/embed\/VwRxOWg?height=450&amp;theme-id=47434&amp;slug-hash=VwRxOWg&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed VwRxOWg\" title=\"CodePen Embed VwRxOWg\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Here&#8217;s another little gotcha! <em>Where do you want to position that close button? <\/em><\/p>\n\n\n\n<p>I would think probably in the top right or top left of the dialog. So you might&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-selector-tag\">dialog<\/span> {\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-attribute\">position<\/span>: relative;\n<\/span><\/mark><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  .close-button {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">position<\/span>: absolute;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-number\">0.5rem<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">right<\/span>: <span class=\"hljs-number\">0.5rem<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Danger danger! <\/p>\n\n\n\n<p>By setting the <code>&lt;dialog&gt;<\/code> to <code>position: relative;<\/code>, something we naturally do when I&#8217;m thinking about using <code>position: absolute;<\/code> on a child element, we&#8217;ve introduced a potentially gnarly UX bug. Desktop browsers will now scroll the page all the way to the top when the dialog is opened, and you&#8217;ll see the dialog open and centered in the viewport. <strong>But on iOS, the window will not scroll up. <\/strong>This leads to a situation where you can open the dialog and&#8230; not see it at all. <\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player wp-block-jetpack-videopress--has-max-width\" style=\"max-width: 318px;\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='351' height='750' src='https:\/\/videopress.com\/embed\/VctU4O9E?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1674852142'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>The problem there is that, by default, the <code>&lt;dialog&gt;<\/code> is <code>position: fixed;<\/code>, which means it would show up no matter where the page is scrolled to (even without force-scrolling to the top). But we&#8217;ve (ok ok, <em>I&#8217;ve<\/em>) accidentally overridden it with <code>relative<\/code> creating this unexpected behavior. <\/p>\n\n\n\n<p>It&#8217;s a little tempting to look into locking the scroll position while the dialog is open, <a href=\"https:\/\/robbowen.digital\/wrote-about\/locking-scroll-with-has\/\">perhaps with a simple <code>:has()<\/code>-based selector<\/a>, but I&#8217;m not sure how much I care if the page can scroll while it&#8217;s open. <\/p>\n\n\n\n<p>There is other little gotcha&#8217;s to think about as well, like the fact that content within dialogs are not find-on-page-able until opened. Which kinda makes sense, but content within details elements are, so it&#8217;s just something you need to know about and probably not accidentally hide content within you want available always. I&#8217;d <a href=\"https:\/\/www.scottohara.me\/blog\/2023\/01\/26\/use-the-dialog-element.html\">listen to Scott O&#8217;Hara<\/a>, myself. <\/p>\n\n\n\n<p>If you want to see a <code>&lt;dialog&gt;<\/code> in production use, look no further. Posts right here on Boost have a &#8220;Take Quiz&#8221; button the sidebar that opens a dialog with a custom Web Component that the Frontend Masters team have built to help build you a custom course path. <\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='395' src='https:\/\/videopress.com\/embed\/YO00uyEs?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1674852142'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>Any other gotchas you&#8217;ve found with the dialog element?<\/p>\n\n\n\n<p>Oh hey ya know what, if someone were to make like 8-10 really cool designs for the <code>&lt;dialog&gt;<\/code> and the <code>::backdrop<\/code>, that would make for a pretty sweet guest post, I&#8217;d say. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>The &lt;dialog&gt; element in HTML is tremendous. We&#8217;ve got support across the board now, so using it is a smart plan. Just with basic usage, you get a centered modal dialog experience that comes up when you call it, a dimmed background, focus trapped within it, closes with the ESC key, and focus returning where [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":738,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[98,31,3],"class_list":["post-724","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-dialog","tag-html","tag-javascript"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/dialog-thumb.jpg?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/724","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=724"}],"version-history":[{"count":11,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/724\/revisions"}],"predecessor-version":[{"id":740,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/724\/revisions\/740"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/738"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=724"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=724"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=724"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}