A look at the dialog element's super powers
- Published at
- Updated at
- Reading time
- 6min
A long time ago in 2014, the dialog
element landed in Chromium 37
. The idea behind the HTML element was to make it easier to build modals, dialogs and popups.
The specification defines the element as follows:
The dialog
element represents a part of an application that a user interacts with to perform a task, for example a dialog box, inspector, or window.
We have 2022 now and almost made it to use the dialog
element! ๐ In March, starting with Safari 15
(Safari release notes) and Firefox 98
(Firefox release notes), both missing browsers ship the element that's been included in Chromium for ages.
This progress brings us to a cross-browser-supported dialog
element in all evergreen browsers soon!
37 | 37 | 79 | 98 | 98 | 15.4 | 15.4 | 3.0 | 37 |
Firefox and Safari shipping the element is excellent for accessibility because building an accessible modal has always been complicated. Read Scott O'Hara's article "Having an open dialog" to learn more about modal accessibility.
I played with this new way to build dialogs and discovered that the HTMLDialogElement
not only differentiates between non-modal and modal dialogs but also comes with modal functionality that's hard to get right.
Let's have a look at the dialog
's super powers!
To get started, drop the element into your pages and find out that ...
HTML
... you won't see anything. The supporting browsers include the following CSS in their user-agent styles.
dialog:not([open]) {
display: none;
}
The dialog
element is hidden by default, and its visibility relies on a present open
attribute. Let's add it to the HTML!
HTML
CSS
And here we have it โ a visible dialog element!
Usually, you would open dialogs via JavaScript, but if you server-render pages after a form submit, defining it in HTML might make sense to show a modal after the page has loaded.
You could now tinker with the open
attribute via JavaScript to show the dialog, but hold on! The element provides two additional methods for dynamic interactions.
To show a dialog as a non-modal, access the dialog
element from the DOM and call its show
method.
const dialog = document.querySelector('dialog');
dialog.show();
// to close the dialog use `close`
dialog.close();
See it in action below.
dialog.show()
opens an absolute position non-modal dialog.
Even though this functionality looks like a simple element display toggle, more things are going on. ๐
The show
method adds the open
attribute to the dialog element, the element is absolute positioned following the user-agent styles, and the closing button is automatically focused.
You don't see mouse-triggered focus states on my site, because it uses :focus-visible
. If you want to see the focus outlines, focus and press the "Open dialog" button with your keyboard.
But browsers are not only moving the focus into the modal when you open it. The focus will be moved back to the previously focused element if you close the modal with dialog
, too. That's pretty handy and very valuable for accessibility and keyboard usability.
Check the spec if you want to learn more about the dialog's focusing logic. But be warned: this part of the specification is not easy to grasp, and there are also discussions happening to change the described focus behavior.
These features are valuable to build non-modal popups, but what about the traditional full-page-covering modals? How do you create the modal page overlay, and how could you prevent users from clicking buttons or links outside the open dialog?
showModal
provides all these functionalities!
To create a modal dialog covering all the other page content, use the showModal
method.
const dialog = document.querySelector('dialog');
dialog.showModal();
// to close the dialog use `close`
dialog.close();
See it in action by clicking the button below.
dialog.showModal()
opens a fixed positioned modal dialog, it's closeable by pressing ESC and all other elements become inaccessible.
The showModal
method differs in multiple ways from show
.
The modal dialog is position: fixed
If you inspect the modal, you'll see that Safari and Chromiums provide the styling via dialog:-internal-modal
or dialog:-internal-modal-dialog
.
The dialog element is now fixed positioned and covers whatever was rendered on the screen.
The modal dialog provides a ::backdrop
pseudo-element
Additionally, the dialog element comes with a ::backdrop
overlay pseudo element when it's opened via showModal
.
/* Included in the Chrome user agent styles */
dialog::backdrop {
position: fixed;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
background: rgba(0, 0, 0, 0.1);
}
Unfortunately, neither Chromium nor Safari show the ::backdrop
pseudo-element in the elements panel yet.
How to close the modal on ::backdrop
click
The number one missing thing I see people pointing out is that the dialog doesn't automatically close when clicking the backdrop. I'd argue that it would have been valuable to put this functionality into the spec, but I'm sure there's a good reason not to.
Luckily you can work around that by adding a click handler to the dialog
element and using the bubbly nature of click events.
dialog.addEventListener('click', (event) => {
if (event.target.nodeName === 'DIALOG') {
dialog.close();
}
});
Click events bubble up the DOM tree. If there's a button
inside a div
inside the html
element, you can attach a click handler to the html
element and react to button clicks.
Adam shared that you can leverage this event behavior and close the dialog when someone clicks the backdrop overlay. If someone clicks on elements inside the dialog element such as buttons or a form, event
will be the particular node name (BUTTON
or FORM
). But if someone clicks the overlay, it'll be DIALOG
because the pseudo-element belongs to the dialog
.
See the nifty little trick in action below.
dialog.showModal()
opens a fixed positioned modal dialog, it's closeable by pressing ESC and all other elements become inaccessible.
But there's more fancy stuff!
The modal dialog can be closed via ESC
There's nothing more to say other than there's no need to add keypress event handlers all over the place. ๐ Also, if the dialog is closed by pressing ESC
, a cancel
event is triggered.
The modal dialog makes everything else inaccessible
If you open a modal dialog, you can't click other elements anymore and that's not only because the backdrop covers them. If you hit the TAB
key, you'll also find that all the other interactive elements are inaccessible.
It's only the open dialog element you can interact with!
The inert
element theoretically provides ways to make elements inaccessible, but there doesn't seem to be any browser movement to implement this attribute anytime soon.
I love seeing more typical UI use cases entering the web platform. And the dialog
element is more than welcome because building accessible modals is much more than toggling the display
CSS property. The more functionality browsers provide, the better for everyone.
The dialog
element is also included in the Interop 2022 initiative, in which browser vendors work together to make building for the web easier for developers.
It's refreshing to see all the browser movement lately. I can't wait to see what's next!
Further reading
Here are more resources if you want to learn more about the dialog
element.
Join 5.5k readers and learn something new every week with Web Weekly.