Build Native Modals Using the Dialog Element

With release of HTML 5.2 comes the dialog element. The dialog element has been around for a while now but was an experimental technology.

Previously, if we wanted to build a modal or a dialog box of any sort, we needed to arrange our markup in a way where we have a backdrop, a close button, keep events trapped within the dialog, find a way to pass message out of the dialog... It was really complicated. The dialog element solves all the problems above and more.

Heads up: I'm going to interchange between modal and dialog a lot.

Table of Contents

    Quick comparison between bootstrap modal and the new dialog element

    <!-- Trigger the modal with a button -->
    <button type="button" class="btn btn-info btn-lg" data-toggle="modal" data-target="#myModal">Open Modal</button>
    
    <!-- Modal -->
    <div id="myModal" class="modal fade" role="dialog">
      <div class="modal-dialog">
    
        <!-- Modal content-->
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal">×</button>
            <h4 class="modal-title">Modal Header</h4>
          </div>
          <div class="modal-body">
            <p>Some text in the modal.</p>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
          </div>
        </div>
      </div>
    </div>

    The markup above is for bootstrap, compare that to this

    <!-- Trigger the modal with a button -->
    <button type="button" class="btn">Open Modal</button>
    
    <!-- Modal -->
    <dialog open>
      Native dialog box!
    </dialog>

    Getting started

    We've seen the simplest markup for a dialog element, there are some basic operations you'll need to understand to get it to work.

    You may have noticed the open attribute on the dialog above. Adding that attribute to the element will force the dialog to show, removing it will do otherwise. The dialog will also be absolutely positioned on the page.

    Default dialog styling

    The dialog element exposes some methods that we can use to manipulate its behavior.

    const modal = document.querySelector('dialog')
    const openBtn = document.querySelector('.open-modal')
    const closeBtn = document.querySelector('.close-modal')
    
    // showModal() makes modal visible (adds `open` attribute)
    openBtn.addEventListener('click', () => modal.showModal())
    
    // close() hides modal (removes `open` attribute)
    closeBtn.addEventListener('click', () => modal.close())

    As you can see from the pen above, the dialog element exposes the showModal() and close() methods. We use these methods to show and hide the modal respectively.

    Sending data out of the modal

    We can use modal.returnValue to send data out of the modal. This way, we have native control over what we transmit out of the modal.

    First, let's send data out of the dialog. We do that by passing the data we want to send through the close() method.

     // close() hides modal (removes `open` attribute and send data out the modal)
    closeBtn.addEventListener('click', () => modal.close('Lorem ipsum'))

    We can capture the data sent out of the modal by calling the modal.returnValue

     closeBtn.addEventListener('click', () => {
      modal.close('lorem')
    
      alert(`modal says: ${modal.returnValue}`)
    })

    Styling the modal

    We would style a dialog just as we would any CSS element... almost any CSS element 😉. The dialog component also brings in a new ::backdrop pseudo selector that we can use to style the backdrop

     dialog {
      padding: 3rem;
      border-radius: 4px;
      border: none;
      box-shadow: 0 4px 6px lightgray;
      transition: transform .5s;
      transform: scale(.1);
    
      &[open] {
        transform: scale(1);
      }
    }
    
    ::backdrop {
      background: rgba(black, .5);
    }

    In Conclusion

    The dialog component is not yet supported by every browser there is. All current variations of Chrome (desktop or mobile) does, but other browsers are lacking.

    To remedy this, we need to polyfill the dialog. Fortunately for us, the team behind Chrome has built one for us. It's called dialog polyfill.

    Samuel Oloruntoba

    39 posts

    Samuel is a multi-disciplinary web developer. Nowadays, he spends most of his time in the browser trying to understand it and squeeze as much performance as he can. He's also a proficient Vue/Laravel developer and absolutely slays at FIFA. You can reach out to him for business or Scotch related matters.