🚀AdvancedAdvanced Interactive Elements

The <dialog> Element

Build native modal and non-modal dialogs with the HTML dialog element, focus management, and the showModal/show/close API.

Overview

The `<dialog>` element is a native HTML component for modal and non-modal dialogs, requiring no third-party libraries. It handles stacking context automatically via the top layer, ships with a built-in ::backdrop pseudo-element, and properly traps focus when opened as a modal. A dialog opened with `showModal()` sits in the browser's top layer above all other content regardless of z-index and creates a true focus trap. A dialog opened with `show()` is non-modal: it displays inline without blocking interaction with the rest of the page. Either variant can be closed with `dialog.close(returnValue)`, the Escape key (modal only), or any button with `method="dialog"` inside a form. Dialog is fully supported in all modern browsers. Polyfills exist for legacy environments but are rarely needed today.

Key Points

  • •`showModal()` creates a top-layer modal with focus trap; `show()` creates an inline non-modal dialog
  • •The `::backdrop` pseudo-element styles the overlay behind a modal dialog
  • •The `close` event fires when the dialog closes; `dialog.returnValue` holds the value of the closing button
  • •A `<form method="dialog">` inside a dialog closes it on submit and sets returnValue to the submitter button's value
  • •Use `autofocus` on the first interactive element inside the dialog for correct focus placement
  • •Escape key automatically closes modal dialogs; you can cancel it via the `cancel` event
  • •The `open` attribute reflects whether the dialog is visible; don't set it directly use the JS methods

Examples

Full modal dialog with open/close and return value

<button id="open-btn">Open Dialog</button>

<dialog id="confirm-dialog" aria-labelledby="dlg-title">
  <h2 id="dlg-title">Confirm Action</h2>
  <p>Are you sure you want to delete this item?</p>

  <form method="dialog">
    <button value="cancel" autofocus>Cancel</button>
    <button value="confirm">Delete</button>
  </form>
</dialog>

<script>
const dialog = document.getElementById('confirm-dialog');
const openBtn = document.getElementById('open-btn');

openBtn.addEventListener('click', () => dialog.showModal());

dialog.addEventListener('close', () => {
  if (dialog.returnValue === 'confirm') {
    console.log('User confirmed deletion');
  }
});
</script>

The form method="dialog" automatically closes the dialog. The clicked button's value becomes dialog.returnValue.

Styled backdrop and dialog transitions

<style>
  dialog {
    border: none;
    border-radius: 12px;
    padding: 2rem;
    max-width: 480px;
    box-shadow: 0 25px 50px rgba(0,0,0,0.25);
  }

  dialog::backdrop {
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(4px);
  }

  /* Animate in */
  dialog[open] {
    animation: slide-in 0.2s ease-out;
  }

  @keyframes slide-in {
    from { opacity: 0; transform: translateY(-20px); }
    to   { opacity: 1; transform: translateY(0); }
  }
</style>

<dialog id="styled-dialog">
  <h2>Settings</h2>
  <p>Configure your preferences below.</p>
  <form method="dialog">
    <button>Close</button>
  </form>
</dialog>

The ::backdrop pseudo-element only appears for modal dialogs opened with showModal().

Preventing close and handling the cancel event

<dialog id="form-dialog">
  <h2>Edit Profile</h2>
  <form method="dialog" id="profile-form">
    <label>Name <input type="text" name="name" required></label>
    <button type="submit" value="save">Save</button>
    <button type="button" id="cancel-btn">Cancel</button>
  </form>
</dialog>

<script>
const dialog = document.getElementById('form-dialog');

// Prevent Escape key from closing if there are unsaved changes
dialog.addEventListener('cancel', (event) => {
  if (hasUnsavedChanges()) {
    event.preventDefault();
    if (confirm('Discard unsaved changes?')) {
      dialog.close('cancelled');
    }
  }
});

document.getElementById('cancel-btn').addEventListener('click', () => {
  dialog.close('cancelled');
});
</script>

Calling event.preventDefault() on the cancel event stops Escape from closing the dialog.

Common Mistakes

  • Using `display:none` / `display:block` to show/hide dialogs instead of the JS methods this bypasses focus management and the top layer
  • Forgetting `aria-labelledby` or `aria-label` on the dialog screen readers need a label to announce the dialog purpose
  • Not using `autofocus` on the first interactive element keyboard users land on the wrong element

Note

For a close button that does not submit a form, use `<button type="button" onclick="dialog.close()">` using `type="submit"` inside `method="dialog"` closes the dialog but also submits any outer form.

Exercises

Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

What is the difference between dialog.show() and dialog.showModal()?