How to Create a Modal Dialog Using Traditional JavaScript and the HTML5 Dialog Element

You can create a modal dialog using either a traditional custom implementation with a <div> and JavaScript or the native HTML5 <dialog> element, with the former offering full styling control and legacy support while the latter provides built-in accessibility features like focus trapping and ESC-key dismissal.

The jisan-mia/dom-projects repository demonstrates both approaches to create a modal dialog, providing developers with a complete reference for implementing overlays that range from fully custom solutions to modern native browser APIs. Whether you need to support legacy Internet Explorer or want to leverage modern accessibility features, understanding both methods ensures you can choose the right implementation for your project requirements.

Traditional Custom Modal Implementation

Architecture and Core Files

The traditional approach in projects/modal/ uses a standard <div> structure transformed into a modal through CSS and JavaScript. The implementation centers around three key files:

The modal() Function

The modal() function in projects/modal/modal.js provides a generic way to convert any wrapper element into a modal dialog. It accepts four parameters: the open button selector, the wrapper selector, the content selector, and a boolean for injecting a close button.

// Initialize the modal
modal(".subscribe", ".subscribe-wrapper", ".subscribe-content", true);

The function performs four critical operations:

  1. Element Selection – Captures references to the trigger button, wrapper overlay, and content container
  2. Class Application – Adds .modal-wrapper and .modal-content classes that apply fixed positioning and centering
  3. Open Handler – Attaches a click listener to the trigger that sets wrapper.style.display = 'block'
  4. Close Logic – Optionally injects a "×" button, handles click-outside-to-close via event.target == wrapper detection, and hides the wrapper with display = 'none'

CSS Styling and Animations

The projects/modal/modal.css file defines the visual behavior of the custom modal. The .modal-wrapper class creates the overlay effect using fixed positioning that covers the entire viewport.

.modal-wrapper {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 1000;
}

.modal-content {
  position: relative;
  background-color: #fff;
  margin: 10% auto;
  padding: 20px;
  width: 80%;
  max-width: 500px;
  border-radius: 8px;
  animation: slideDown 0.3s ease-out;
}

Because this approach uses standard <div> elements, you have complete freedom to define custom animations, transitions, and responsive behaviors without fighting against native browser defaults.

Native HTML5 Dialog Element

Built-in Modal Behavior

The projects/dialog/ directory demonstrates the modern approach using the native <dialog> element. Unlike the custom implementation, the HTML5 dialog provides several accessibility features automatically:

  • Focus trapping – When opened with showModal(), keyboard focus remains inside the dialog
  • Backdrop handling – The browser renders a native backdrop that can be styled with the ::backdrop pseudo-element
  • ESC key dismissal – Pressing Escape automatically closes the modal without additional JavaScript

JavaScript API

The projects/dialog/dialog.js file shows the minimal JavaScript required to control the dialog. The API consists of two primary methods: showModal() to open and close() to dismiss.

// projects/dialog/dialog.js
const showBtn = document.getElementById("show-dialog");
const dialog = document.getElementById("dialog");
const closeBtn = document.getElementById("close-dialog");

// Open the modal
showBtn.addEventListener("click", () => dialog.showModal());

// Close the modal
closeBtn.addEventListener("click", () => dialog.close());

The markup structure in projects/dialog/index.html uses the <dialog> tag directly:

<dialog id="dialog" class="subscribe-wrapper">
  <div class="subscribe-header">
    <h2>Be the first to know when new content is available</h2>
    <button id="close-dialog" class="dialog-close-btn">×</button>
  </div>
  
  <div class="subscribe-body">
    <p>Sign up to receive tips and tricks on how to create online designs</p>
    <form id="subscribe-form">
      <input type="email" placeholder="Your email address" required />
      <button type="submit" class="primary-button">Sign me up</button>
    </form>
  </div>
</dialog>

Browser Support Considerations

The HTML5 <dialog> element requires modern browser support. It works in Chrome 37+, Edge (all versions), Firefox 53+, and Safari 15.4+. For older browsers, you must include a polyfill or fall back to the traditional custom modal approach demonstrated in projects/modal/.

Comparing the Two Approaches

When deciding how to create a modal dialog, consider the following technical differences:

Traditional Custom Modal (projects/modal/)

  • Browser support: Works in all browsers including Internet Explorer
  • JavaScript complexity: Requires manual handling of open/close logic, outside clicks, and focus management
  • Styling flexibility: Complete control over animations, transitions, and responsive behavior via CSS
  • Accessibility: Must manually implement aria-modal, aria-labelledby, and focus trapping

Native HTML5 Dialog (projects/dialog/)

  • Browser support: Modern browsers only (Chrome 37+, Firefox 53+, Safari 15.4+)
  • JavaScript complexity: Minimal API surface with showModal() and close() methods
  • Styling flexibility: Limited to browser defaults for backdrop and positioning, though ::backdrop pseudo-element allows some customization
  • Accessibility: Native focus trapping, ESC-key dismissal, and semantic dialog role built-in

Summary

  • The traditional custom modal in projects/modal/ uses a <div> wrapper with the modal() function in modal.js to handle display toggling, outside-click closing, and optional close button injection, offering full browser compatibility and styling control.

  • The HTML5 <dialog> element in projects/dialog/ leverages the native showModal() and close() methods with minimal JavaScript, providing built-in accessibility features like focus trapping and ESC-key dismissal, but requires modern browser support.

  • Choose the custom implementation when you need to support legacy browsers or require complex custom animations, and choose the native dialog for simpler use cases where modern browser support is guaranteed.

Frequently Asked Questions

What is the difference between showModal() and show() for the HTML5 dialog element?

The showModal() method displays the dialog as a modal, blocking interaction with the rest of the page and rendering a backdrop, while show() displays the dialog non-modally without blocking page interaction or adding a backdrop. For true modal behavior as implemented in projects/dialog/dialog.js, always use showModal().

How does the traditional modal handle closing when clicking outside the content?

The modal() function in projects/modal/modal.js attaches a click event listener to the wrapper element and checks if event.target == wrapper. When this condition is true—meaning the user clicked on the backdrop rather than the content—it sets wrapper.style.display = 'none' to hide the modal.

Can I use CSS animations with the HTML5 dialog element?

Yes, though with limitations. You can animate the dialog itself using standard CSS transitions and keyframes, but the ::backdrop pseudo-element has restricted animation capabilities in some browsers. For complex entrance and exit animations as shown in projects/modal/modal.css, the traditional custom modal approach provides more reliable control over every animation aspect.

Is the HTML5 dialog element accessible for screen readers?

The native <dialog> element provides better accessibility defaults than a generic <div>, automatically applying the dialog role and managing focus trapping. However, you must still add proper aria-labelledby attributes pointing to the dialog title and ensure focus is set appropriately when opening. The custom modal in projects/modal/ requires manual implementation of all ARIA attributes and focus management to achieve equivalent accessibility.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →