Dialogs and modals are the essential components for any web application. Still, it takes quite an effort for us to create and define default behavior logic for it using the existing div
element. But with dialog
element, things can be easier. Let's check it out in this post, shall we?
Introducing dialog
element
Dialog element dialog
is a pretty new HTML5 element, and you can use it with the <dialog>
markup, as shown in the example below:
<dialog>
<div>
I'm a dialog!
</div>
</dialog>
Unlike other HTML5 elements, the dialog
element comes with built-in APIs and some basic CSS stylings. By design, the dialog
element has two states - visible (open) or hidden (close), indicated by a single property open
:
<dialog open>
<div>
This dialog is opened and visible!
</div>
</dialog>
<dialog>
<div>
This dialog is hidden!
</div>
</dialog>
The initial state of dialog
is "hidden". The dialog
element exposes the following API methods to control its conditions:
-
showModal()
- open the dialog by setting the attributeopen
to the element. It also adds a::backdrop
pseudo-element to cover the content outside of the element as an overlay effect. -
show()
- similar likeshowModal
, but without adding a backdrop. This method is useful for using thedialog
as a toast notification. -
close(newReturnValue)
- close the dialog by removing theopen
attribute, and updating thereturnValue
of thedialog
element if there is anynewReturnValue
passed.
With the given APIs above, let's implement a simple dialog next.
Create a simple dialog
We are going to create a simple dialog containing three sections:
-
header
, which has the dialog title and a cancel button (with an X indicator) -
div
section where we display all the content for the dialog. In our example, it will be just a simple string. -
footer
contains amenu
of action buttons, such as Cancel and Confirm buttons.
A sample sketch of our dialog is as below:
Now let's add a dialog
markup element with the above sections to the body
section of an HTML
file. The dialog
has an id
attribute with the value main-dialog
, as shown below:
<html>
<body>
<dialog id="main-dialog">
<header>
<h2>Dialog header</h2>
<button id="cancel-btn">X</button>
</header>
<div>
Dialog content
</div>
<footer>
<menu>
<button id="cancel-btn">Cancel</button>
<button id="confirm-btn">Confirm</button>
</menu>
</footer>
</dialog>
</body>
</html>
Next, we add to the body
a main
element that contains a button
to open the dialog:
<body>
<main id="app">
<button id="open-dialog-btn">Open dialog</button>
</main>
<dialog id="main-dialog">
<!--....-->
</dialog>
</body>
When running the above code on the browser, you won't see the dialog as it is not visible by default. We need to bind the onclick
event of the button
with id
"open-dialog-btn" using the addEventListener
method as follows:
//1. Query for open dialog button
const openBtn = document.getElementById('open-dialog-btn');
//2. Query for dialog element
const dialog = document.getElementById('main-dialog');
//3. Add event listener to open dialog on click
openBtn.addEventListener('click', () => {
dialog.showModal();
})
Refresh the browser, and you now can see the dialog is open whenever you click the Open dialog button:
Once the dialog is open, we should bind the other menu buttons and the header button to the proper event handlers and close the dialog with dialog.close()
accordingly.
//1. Query for the buttons
const cancelBtn = document.getElementById('cancel-btn');
const confirmBtn = document.getElementById('confirm-btn');
const headerCancelBtn = document.getElementById('cancel-btn');
//2. Bind to onclick event
cancelBtn.addEventListener('click', () => dialog.close());
confirmBtn.addEventListener('click', () => dialog.close());
headerCancelBtn.addEventListener('click', () => dialog.close())
We now have the component fully workable.
What about accessibility? Does dialog
offer any accessibility support? Let's find out next.
Accessibility with dialog
An accessible dialog needs the following characteristics:
- When the dialog is open using keyboard navigation, the browser should auto-focus on the first button within the dialog. In our case, it is the close button at the dialog header.
- After closing the dialog, the browser should restore the focus on the button we used to open the dialog, allowing users to resume their navigation flow onwards.
- The screen reader should be able to announce the dialog's label, and optionally, its description when entering the dialog.
To set the focus on the first visible button in the dialog, we use the dialog's autofocus
attribute. For its label and description, we can assign the WAI ARIA attributes aria-labelledby
and aria-describedby
to the target elements' id selectors, respectively. We also add role="dialog"
(or role="alert"
) for the screen readers to announce the dialog properly, as seen in the code below:
<dialog
id="main-dialog"
autofocus
role="dialog"
aria-describedby="content-area"
aria-labelledby="label-area"
>
<header>
<h2 id="label-area">Dialog header</h2>
<button id="dialog-header--cancel-btn">X</button>
</header>
<div id="content-area">
Dialog content
</div>
<!-- ... -->
</dialog>
One nice thing about using dialog
element is that when user closes the dialog, the browser automatically restores the focus on the button we used to open it. And that applies with ESC key events, too!
Below is a demonstration of how the navigation keyboard and focus look like with dialog element:
Note
If you want to add an animation effect on closing and opening dialog programmatically, note that you will lose this built-in feature support and have to implement the tab navigation focus yourself.
So far, so good? Next, we will look at how we use the form inside dialog.
Using forms in dialogs
The dialog
element provides built-in support for form
. To enable the connection between dialog and its nested form, we set the method
attribute of the form
element to dialog
. Then we wrap the original content within the form
element, as seen below:
<dialog
id="main-dialog"
autofocus
<!--...-->
>
<form method='dialog'>
<!--The rest of the code -->
</form>
</dialog>
By doing so, triggering any form's button elements (except the button with type='reset"
) will also close the dialog automatically, instead of manually binding the close()
function on each of them.
We can now remove all the onclick event listeners for closing the dialog and it should function as same as previously.
Cool, right? What if you want to trigger additional logic whenever the dialog is closed from outside of the dialog? For that, we use a close
event listener.
Listening to the close
event of a dialog
dialog
has a close
event that we can attach an event handler to using the following syntax:
dialog.addEventListener('close', () => {
//your handler logic
})
Whenever a user closes the dialog, it will trigger this handler. We then can use value
attribute in the buttons and access them using dialog.returnValue
to decide whether the user clicks on a Cancel button or a Submit button, as follows:
<menu>
<button id="dialog-footer--cancel-btn" value="cancel">Cancel</button>
<button id="dialog-footer--cancel-btn" value="confirm">Confirm</button>
</menu>
Within the close
event listener logic, we can perform a simple conditional logic:
dialog.addEventListener('close', () => {
if (dialog.returnValue === 'cancel') {
//do something
} else if (dialog.returnValue === 'confirm) {
//do something
}
})
Note here that if the user hits the ESC key, there won't be any value for returnValue
.
That's it! We have created our first dialog using the dialog
element.
Demo
The demo code is available for trying. ๐
Browser support
There is full support for dialog
and its APIs in all major browsers except Internet Explorer. We now can enjoy the benefit of a built-in dialog element and start customizing it to our needs. Awesome, isn't it?
Summary
We have gone through the basics of building a dialog component using HTML and DOM APIs in this post. With the dialog
element, composing a reusable Dialog component in the front-end framework becomes more straightforward. You can also use the same approach to create a Modal or Toast notification component.
Next, how about trying to make a Dialog component in your favorite framework like Vue or React using the dialog
element? It should be fun ๐!
๐ If you'd like to catch up with me sometimes, follow me on Twitter | Facebook.
๐ Learn about Vue with my new book Learning Vue. The early release is available now!
Like this post or find it helpful? Share it ๐๐ผ ๐
Top comments (7)
Good one, thanks for sharing!
ะกongratulations ๐ฅณ! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up ๐
thank you! ;)
Love the inclusion of accessibility in this article!
Thank you ๐ฅฐ
Nice post๐๐ป
Thank you ๐