Browse by section

Web Design 日本語

Build a Modal Window: jQuery or the dialog Element

This article covers building a modal window with jQuery, and the newer approach that needs almost no JavaScript at all.

The short answer: use the HTML <dialog> element. Escape-to-close, the backdrop, focus trapping and scroll locking all come built in. If jQuery is already on the page, or you need precise control over the open and close behaviour, the jQuery version below still applies.

Both are covered so you can pick the one that fits.

Sponsored

The modal we are building

See the Pen
jQuery modal window
by rin (@rinblog0408)
on CodePen.

The HTML structure

<div id="myModal" class="modal">
  <div class="modal-content">
    <div>
      <p>Modal content goes here</p>
    </div>
    <button id="closeModalBtn">Close</button>
  </div>
</div>

<button id="openModalBtn">Open the modal</button>

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

Check the jQuery version you load. Versions before 3.5.0 carry two XSS vulnerabilities (CVE-2020-11022 and CVE-2020-11023). On a public site, load 3.5.1 or later.

Sponsored

The CSS

.modal {
  display: none; /* hidden initially */
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
}

.modal-content {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: #fff;
  padding: 20px;
}

Opening and closing with jQuery

$(document).ready(function() {
  $("#openModalBtn").on("click", function() {
    $("#myModal").show();
  });

  $("#closeModalBtn").on("click", function() {
    $("#myModal").hide();
  });
});

Three things this is missing

That code produces something that looks like a modal but does not behave like one.

▼What is missing

Missing behaviour Consequence
Escape does not close it No way to dismiss it from the keyboard
Clicking the backdrop does nothing Users hunt for the close button
Focus escapes to the page behind Tab moves out of the modal entirely

The first two are straightforward to add.

$(document).ready(function() {
  const $modal = $("#myModal");

  function openModal() {
    $modal.show();
    // move focus into the modal
    $modal.find("button").first().trigger("focus");
  }

  function closeModal() {
    $modal.hide();
    // return focus to the trigger
    $("#openModalBtn").trigger("focus");
  }

  $("#openModalBtn").on("click", openModal);
  $("#closeModalBtn").on("click", closeModal);

  // close when the backdrop is clicked
  $modal.on("click", function(e) {
    if (e.target === this) closeModal();
  });

  // close on Escape
  $(document).on("keydown", function(e) {
    if (e.key === "Escape" && $modal.is(":visible")) closeModal();
  });
});

The e.target === this check matters. Without it, clicking anything inside the modal also closes it.

Trapping focus inside the modal is considerably more work to write correctly. If you need that, the <dialog> element gets you there faster.

Sponsored

The dialog element needs almost no JavaScript

The HTML <dialog> element handles Escape, the backdrop, focus management and scroll locking for you.

<dialog id="myDialog">
  <p>Modal content goes here</p>
  <form method="dialog">
    <button>Close</button>
  </form>
</dialog>

<button id="openDialogBtn">Open the modal</button>
const dialog = document.getElementById("myDialog");

document.getElementById("openDialogBtn").addEventListener("click", () => {
  dialog.showModal();
});

Three lines of JavaScript.

Use showModal(), not show(). Both display the dialog, but only showModal() makes the rest of the page inert — which is what “modal” means.

  • showModal(): the page behind becomes inert (modal)
  • show(): the page behind stays interactive (modeless)

Wrapping the close button in <form method="dialog"> closes the dialog with no JavaScript at all.

Style the backdrop with the ::backdrop pseudo-element:

#myDialog {
  border: none;
  border-radius: 8px;
  padding: 20px;
}

#myDialog::backdrop {
  background-color: rgba(0, 0, 0, 0.5);
}

To close on a backdrop click, add a few lines:

dialog.addEventListener("click", (e) => {
  if (e.target === dialog) dialog.close();
});

The dialog’s own content sits inside it, so e.target is only the dialog itself when the backdrop was clicked.

Which to choose

Use <dialog> for new work, and the jQuery version when adding to an existing site.

▼The two approaches compared

<dialog> CSS + jQuery
JavaScript Three lines Open, close, Escape, backdrop
Escape to close Built in You write it
Backdrop ::backdrop You style it
Focus trapping Built in Hard to get right
Scroll locking Built in You write it
Fine-grained control Limited Complete

Focus is where the gap is widest. A hand-rolled modal lets Tab move into the page behind it, which leaves keyboard users in a dialog they cannot dismiss and content they cannot reach. <dialog> prevents that automatically.

The same show-and-hide reasoning appears in building an accordion menu and building a jQuery dropdown menu, both of which also cover the native HTML alternatives.

For walking from a clicked element to the thing it controls, getting parent elements in jQuery explains when .closest() is the right tool.