At work, my talented colleague Anda and I have been taking a fresh look at modal dialogues with a view to modernising relevant design system components. During this project I’ve expanded my knowledge of the HTML dialog element, especially the nitty-gritty of using it in practice.
I’ve really enjoyed learning that we’re now in an era where you have the option to both launch and dismiss a dialogue without any reliance on JavaScript. That’s good news not only for resilience but also for lean development and maintenance. It’s also just cool – go HTML!
To let users dismiss a dialog
we can nest inside it an implicit or explicit button type=submit
inside a form method=dialog
. Activating this button causes the dialog box to close, the states of any other form controls to be saved but not submitted, and sets the dialog.returnValue
to the value
of the button. Here’s an example:
<dialog>
<p>Hello world</p>
<form method="dialog">
<button>Close</button>
</form>
</dialog>
At first this felt nice but not particularly useful in real life, since dialogues often contain “real forms” that might typically need a method of POST. I foresaw issues in having to nest one form inside the other which isn’t feasible, and also I imagined you’d typically want a second dismissal button (perhaps labelled Cancel) at the bottom of the “real form” and would want that to reuse the same dismissal approach which might be tricky if it’s not directly inside the form method=dialog
.
However I can see two ways around the issues. Firstly you could have both forms, non-nested, and still make one of the buttons in the main form act as a “close” button for the method=dialog
form by judicious use of button’s form
attribute. Check out my pen: Putting the submit button of a `form method=dialog` inside another “main” form. As an alternative option, I believe you could even dispense with the form that uses method=dialog
by applying formmethod=dialog
to buttons responsible for dismissing the dialog. That’d allow you to just have the single “main” form.
When thinking about launching dialogues I discovered the new Invoker Commands API. This gives the button
element new attributes that we can use to declaratively associate the button with another element such as a dialog
. We can additionally specify a method of that associated element that we want executed when our button is activated. Here’s an example:
<button commandfor="mydialog" command="show-modal">Show modal dialog</button>
<dialog id="mydialog">
Dialog Content
</dialog>
Taking things a step further, you could also use the Invoker API to create the button(s) for dismissing the dialog
too, rather than the form-based approach I mentioned previously.
<dialog id="mydialog">
<button commandfor="mydialog" command="close">Close</button>
Dialog Content
</dialog>
At present, the commandfor
attribute does not yet work across all the major browsers. So right now we won’t be using the Invoker API in production – or if we do it’d only be in cases where we’re using using JS to create a button
and can first do feature detection to check if the user’s browser supports invoker commands. For now we’ll likely find other ways to launch dialogues, and if we want a non-JS-reliant way to close a dialog
we’d reach for the “form method” approach.
But in general the future of powerful, declarative HTML looks bright and full of opportunities.