HTML Dialog and nextSimblingElement

I love it when you get a job that allows you to explore a new feature of HTML and also gives you a chance to explore some ideas. The brief was to implement the content of a field that had extra information in a lightbox, a commonly used word for a modal dialog, or in normal English a pop-up window. This as such isn't a hard thing but I wanted the code to work on all links in the component. There were some ways I could have used since the site is built in Drupal but these all passed a URL to the href to populate the dialog modal, in practice, it was opening a new route or page, and I wanted something simpler. The extra text was already rendered on the page as it is a field associated with the element. The markup I had was something like this in a template.

<article>  
  <h2>How to use HTML dialog element</h2>
  <p>Greetings, one and all!</p>
</article>

The basic markup for an HTML dialog

The first thing I did was hide the extra text field by wrapping it in an HTML dialog element. And then I added the read more button with a class of modalOpenButtonSingle.

<article>  
  <h2>How to use HTML dialog element</h2>

  <button class="modalOpenButtonSingle">Read more</button>

  <dialog>
    <p>Greetings, one and all! This extra info.</p>
  </dialog>

</article>

If you want to have the dialog open on load, then you can add the open attribute to the dialog like so.

<dialog open>
  <p>Greetings, one and all! This extra info.</p>
</dialog>

But of course, we want the dialog to be closed on page load so the rest of the article will be based on that scenario. Onward!

Adding the Magic (JavaScript) to open the HTML dialog modal

And, to make this all work we would use some code like the following.

const modalButton = document.querySelector('.modalOpenButtonSingle');

// Single
modalButton.addEventListener('click', () => {

  const modalWindow = document.querySelector('.modalWindow');

  if (typeof modalWindow.showModal === "function") {
    modalWindow.showModal();
  } else {
    outputBox.value = "Sorry, the <dialog> API is not supported by this browser.";
  }

});

This as a one-off item is fairly simple to make into a pop-up window but when you have many of these article elements and I want to attach the click event only to the clicked element I had to explore further, a good Friday night challenge was at hand. The markup for this will be something like this.

<article>  
  <h2>How to use HTML dialog element</h2>

  <button class="moreDetails">Read more</button>

  <dialog>
    <p>Greetings, one and all! This extra info.</p>
  </dialog>

</article>

<article>  
  <h2>How to have a good time at a party</h2>

  <button class="moreDetails">Read more</button>

  <dialog>
    <p>Greetings, one and all! This extra info.</p>
  </dialog>

</article>

The first thought was that I would somehow need to use JavaScript this and then associate the clicked button with the associated dialog but in the end, I tapped into my jQuery knowledge and the (click)event target and implemented what I think is a smart solution.

querySelectorAll and foreach

The first thing we need to do is select all the links that will open the modal. Since we had added a class to the button in the twig template we can use querySelectorAll to select all the elements.

const modalButtons = document.querySelectorAll('.moreDetails');

Next, we need to use a for each to loop through each element in the array that is created by using querySelectorAll and then add a click event on each of the buttons. We also pass the event into the event.

modalButtons.forEach((b) => {
  b.addEventListener('click', (event) => {

    const inner = event.target.nextElementSibling;

    if (typeof inner.showModal === "function") {
      inner.showModal();
    } else {
      outputBox.value = "Sorry, the <dialog> API is not supported by this browser.";
    }
    
  });
});

In the basic example, you will see that we create a variable of the modal, this line const modalWindow = document.querySelector('.modalWindow');, so we can target it. However, if we do this when we have many dialog elements we will only be able to target the first one. I.e. It wouldn't matter which button you clicked you would always open the first dialog. Somehow I needed to associate the read more button with the related dialog, this is where my jQuery knowledge came in, the need to find an element close by in the DOM. Traversing the DOM is something jQuery is extremely good at but was there a vanilla JavaScript option? Of course, there was but I'd never used it.

Finding an element using nextElementSibling

Since we passed the event into the click function we can use the event object and its target property. Since we also know that the button is followed by the dialog we want to show we can use nextElementSibling to locate it.

Once we have the dialog located and assigned to a constant we can then use showModal to show it. I have used the if block from MDN to check that the API is supported in the browser and if not print the message.

Creating a Close Button

Inside the dialog element, we can use a form with a method of dialog to create a close button and wala we have our complete HTML dialog modal working.

<form method="dialog">
  <button>Close</button>
</form>

That's it for the nuts and bolts, just a bit of fine-tuning and styling.

Styling an HTML Dialog Modal

All CSS can be used to style an HTML dialog element so I won't go into details here. The default look is fairly bare bones, it renders with a black border and adds padding and centres it. One nice feature of the HTML Dialog is the ::backdrop pseudo element that is included and can be styled. Add it to the global scope or if you want to add it to a specific dialog you can use the dialog class with the ::backdrop pseudo-element. Either of the following will work.  

/* Apply to page */
::backdrop {
  background-color: hsla(223, 100%, 88%, 0.5);
}

/* dialag.class::backdrop {} works */
.backdrop::backdrop {
  background-color: hsla(87, 56%, 56%, 0.5);
}

The Final Solution

See the Pen HTML Dialogw with and nextSimblingElement by Simon (@simonramsay) on CodePen.

Accessibility

The nice thing about using the dialog is the accessibility. If you use a button to open it, it is automatically added to the TAB order so it comes into focus using TAB. It also closes with the ESC button with no need to do anything. Magic! Click Trapping and TAB order.

Currently, HTML dialog support is at close to 93%, so if you wanted you could add a fullback click function and add a class to the dialog and then control its visibility with CSS. There would be extra work involved so it would depend on if you needed to support the browsers that don't support it.

That's it for this HTML Dialog and JavaScript snippet. If mucking around with JavaScipt, front-end development and design interests you be sure to sign up for my newsletter, sign up below. Until next time. Seize the day!