DEV Community

Cover image for Native HTML: Dialog boxes
Andrew Bone
Andrew Bone

Posted on

Native HTML: Dialog boxes

For years if you wanted to make a modal dialog box you'd have to make a div, hide it and then display it when the user did a certain action. This was easy to throw together but accessibility wasn't great, a common error was leaving elements of the dialog in the tab index meaning a user would have to tab through every element of the dialog before they could move on.

As you've probably worked out I'll be writing about the native dialog element and how I use it. At the time of writing, support is, unsurprisingly, not all that great.

support table

The dialog element

I always like to take a dive into Mozilla's dev site to help learn about new elements so here is how they describe it.

The HTML <dialog> element represents a dialog box or other interactive component, such as an inspector or window.

OK, but what does that mean? The syntax looks simple enough so let's give it a go.

<dialog open> Test dialog </dialog>
Enter fullscreen mode Exit fullscreen mode

How it looks in a supported browser.

Simple Example

And there we have it, thanks for read... Oh, we're not quite done yet. We need to interact with the dialog, having it there all the time really isn't that helpful. Mozilla gives us a usage example but no information to go with it, so let's look elsewhere.

The w3 spec

When in doubt head to the spec and sure enough we have 3 functions for interacting with the dialog and they each have descriptions.

dialog . show( [ anchor ] )
Displays the dialog element.
The argument, if provided, provides an anchor point to which the element will be fixed.

dialog . showModal( [ anchor ] )
Displays the dialog element and makes it the top-most modal dialog.
The argument, if provided, provides an anchor point to which the element will be fixed.
This method honors the autofocus attribute.

dialog . close( [ result ] )
Closes the dialog element.
The argument, if provided, provides a return value.

Putting this in practice

Now, this is my own way of putting this into practice if you think it shouldn't be done this way let me know.

With labels you have a for attribute that lets you point to an element, by ID, that you want the label to be connected with. This inspired me to have a data-modal-for, data because it's not a real attribute.

<button data-modal-for="testDialog" open>Open modal</button>
<dialog id="testDialog">
  <h1>An example of a native Dialog element</h1>
  <p>This is a dialog box and, believe it or not, it's built into HTML, sure we needed some javascript to open it but hey, it's a start.</p>
  <button data-modal-for="testDialog" close>Close modal</button>
</dialog>
Enter fullscreen mode Exit fullscreen mode
dialog {
  border: none;
  border-radius: 2px;
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
  max-width: 60vw;
}
Enter fullscreen mode Exit fullscreen mode
const modalTriggers = document.querySelectorAll(`[data-modal-for]`);

for (let trigger of modalTriggers) {
  const modal = document.getElementById(trigger.dataset.modalFor);
  trigger.addEventListener('click', () => {
    trigger.hasAttribute('open') && modal.showModal();
    trigger.hasAttribute('close') && modal.close();
  });
}
Enter fullscreen mode Exit fullscreen mode

Here's how it looks after pressing the button, in a supported browser.

Dialog example

Explain the javascript

I'm going to presume you know what the javascript does so I'm going to explain why I've done it this way.

First, I select all the elements I've given the data-modal-for attribute to then I add eventListeners to them. I also give my trigger elements an open or close attribute to specify how they will change the dialog

I know that this means any dynamically created elements won't work out the box but this was still the best way I could think of.

Signing off

Let me know if you like these things we can't really use yet but maybe one day posts. I find the advancement of the platform interesting and enjoy sharing.

Thanks for reading 🦄🦄😀😀❤🦄

Top comments (12)

Collapse
 
lexlohr profile image
Alex Lohr

I dislike the dialog element a bit, because it is unusable without JS or a reload of the page, unlike details/summary or form validation constraints, so the only advantage over the current solution is that it's a bit more semantic.

As for your script, I would prefer a global event handler so you don't have to re-run it after changes in the page:

document.addEventListener('click', (ev) => {
  let trigger = ev.target;
  let modalId;
  while (trigger && !(modalId = trigger.getAttribute && trigger.getAttribute('data-modal-for'))) {
    trigger = trigger.parentNode;
  }
  if (modalId) {
    const modal = document.getElementById(modalId);
    trigger.hasAttribute('open') && modal.showModal();
    trigger.hasAttribute('close') && modal.close();
  }
});

Collapse
 
link2twenty profile image
Andrew Bone • Edited

I agree that having to rely on JS is a pain, I feel like there should be some native modal-for like functionality.

I always worry having an event fired for every click will have a big impact on performance.

Collapse
 
lexlohr profile image
Alex Lohr

It's a small trade-off, as far as I'm concerned.

In the worst case, it will traverse the DOM from the clicked element to the html tag for every click and get the attribute in question, which modern browsers can easily do within a few milliseconds even for an html structure that is over 200 nodes deep.

At the same time, you'll save memory for the listeners when you can do it with one of them - and, as I said, it is a good solution to DOM changes that introduce new dialog elements. Also, you can run it even before dom.ready, which adds even more flexibility.

Thread Thread
 
link2twenty profile image
Andrew Bone • Edited

There is an issue on the spec's GitHub, no idea if it will go anywhere but may as well have a look.

Have some way of opening `` elements without JavaScript #3567

keithamus avatar
keithamus commented on Mar 14, 2018

dialog elements are a great addition and I'm glad they're getting implemented, but a key part of their functionality relies on JavaScript: to open a <dialog> you need to use JavaScript to set the open attribute.

It'd be great if there was a way to make a <a> or a <button> elements capable of opening dialogs.

Precident already exists for page interactivity baked into HTML - for example <a href="#.."> can already scroll the page, and <details> elements are capable of hiding elements behind interactivity, so I think it stands to reason <dialog> elements could be opened by other page elements.

Thread Thread
 
lexlohr profile image
Alex Lohr

I have added a comment to your proposal.

Thread Thread
 
link2twenty profile image
Andrew Bone

I saw that 😀

Thread Thread
 
link2twenty profile image
Andrew Bone • Edited

Worth noting HTML is not case sensitive so the opensModal attribute would have to be opens-modal

Collapse
 
konrud profile image
Konstantin Rouda

Nice article. My 50cents. Instead of using data-modal-for you could use aria-controls attribute, this way make your HTML a bit more accessible.

Aria-controls attribute identifies the element (or elements) whose contents or presence are controlled by the current element.

Collapse
 
astopo profile image
astopo

neat :)

Collapse
 
baohx profile image
Gordon Forsythe

Looks like there is a polyfill for other browsers github.com/GoogleChrome/dialog-pol...

Collapse
 
gazelle0130 profile image
gazelle

nice

Collapse
 
qm3ster profile image
Mihail Malo

Your screenshots tricked me into clicking them instead of the codesandbox.
Twice.
Shame on me.