W3docs

Etiqueta HTML <dialog>

Aprende la etiqueta HTML <dialog>: show() vs showModal(), el patrón form method="dialog", eventos close y cancel, y accesibilidad.

La etiqueta <dialog> es uno de los elementos HTML5. Crea un cuadro de diálogo nativo — un panel emergente como un mensaje de confirmación, una alerta o un formulario — con el que el usuario interactúa antes de continuar. El diálogo está oculto por defecto y se muestra u oculta a través de una pequeña API de JavaScript.

Esta página cubre las dos formas de abrir un diálogo (show() y showModal()), cómo cerrarlo y leer su resultado, el patrón <form method="dialog">, los eventos close y cancel, la accesibilidad y el estilo del backdrop.

¿Por qué usar <dialog> nativo en lugar de un modal con <div> personalizado?

Antes de <dialog>, los desarrolladores construían modales manualmente con un <div> más un conjunto de CSS y JavaScript. El elemento nativo hace ese trabajo por ti y lo hace de forma más fiable:

  • Trampa de foco integrada. Cuando se abre con showModal(), el foco del teclado (Tab / Shift+Tab) se mantiene dentro del diálogo. Un modal con <div> tiene que atrapar el foco manualmente, y hacerlo mal es un error de accesibilidad común.
  • Apilamiento en capa superior. Un diálogo modal se renderiza en la capa superior del navegador, por lo que siempre se pinta encima del resto de la página independientemente del z-index. Se acabaron las guerras de z-index.
  • Backdrop automático. showModal() dibuja un pseudo-elemento ::backdrop detrás del diálogo que bloquea los clics en la página subyacente — no se necesita ningún elemento de superposición extra.
  • Cerrar con Escape. Presionar Esc cierra un diálogo modal automáticamente (disparando un evento cancel al que puedes reaccionar).
  • Semántica real. El elemento tiene un role="dialog" implícito, por lo que la tecnología de asistencia lo anuncia correctamente sin ARIA adicional.

Sintaxis

La etiqueta <dialog> va en pares. Coloca el contenido del diálogo entre las etiquetas de apertura (<dialog>) y cierre (</dialog>). Lo abres desde el script con uno de dos métodos en el objeto DOM del elemento:

  • dialog.show() — abre un diálogo no modal.
  • dialog.showModal() — abre un diálogo modal.
  • dialog.close([returnValue]) — cierra el diálogo y opcionalmente registra un valor de retorno.

El atributo boolean open refleja si el diálogo está actualmente visible. Generalmente no lo estableces manualmente; los métodos anteriores lo gestionan por ti.

Diálogos no modales: show()

show() abre el diálogo en su lugar, sin bloquear la página. El resto del documento permanece interactivo, no hay ::backdrop, el foco no queda atrapado y Esc no lo cierra. Esta es la opción correcta para paneles no críticos y descartables, como una pequeña caja de herramientas, una nota de "novedades" o un selector en línea.

<!DOCTYPE html>
<html>
  <head>
    <title>Non-modal dialog with show()</title>
  </head>
  <body>
    <button id="open">Open non-modal dialog</button>

    <dialog id="info">
      <p>You can still click and scroll the page behind me.</p>
      <button id="close">Close</button>
    </dialog>

    <script>
      const dialog = document.getElementById("info");

      document.getElementById("open").addEventListener("click", () => {
        dialog.show();
      });

      document.getElementById("close").addEventListener("click", () => {
        dialog.close();
      });
    </script>
  </body>
</html>

Diálogos modales: showModal()

showModal() abre el diálogo como un modal. Esto cambia el comportamiento de cuatro formas importantes:

  1. La interacción está bloqueada. Todo lo que está fuera del diálogo se vuelve inerte — los clics y la entrada de teclado no pueden llegar a la página que hay detrás.
  2. Aparece un backdrop. El navegador renderiza el pseudo-elemento ::backdrop detrás del diálogo, que puedes estilizar.
  3. El foco queda atrapado. La tabulación circula solo entre los elementos enfocables dentro del diálogo.
  4. Se une a la capa superior. El diálogo se pinta por encima de todo el contenido, ignorando el z-index. Presionar Esc lo cierra.
<!DOCTYPE html>
<html>
  <head>
    <title>Modal dialog with showModal()</title>
    <style>
      dialog {
        width: 40%;
        border: 1px solid #ccc;
        border-radius: 0.5em;
        padding: 1em;
      }
      dialog::backdrop {
        background-color: rgba(0, 0, 0, 0.5);
      }
    </style>
  </head>
  <body>
    <button id="open">Open modal dialog</button>

    <dialog id="confirm">
      <p>The page behind is blocked. Press Esc or a button to close.</p>
      <button id="close">Close dialog</button>
    </dialog>

    <script>
      const dialog = document.getElementById("confirm");

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

      document.getElementById("close").addEventListener("click", () => {
        dialog.close();
      });
    </script>
  </body>
</html>
Result

El patrón <form method="dialog">

Un <form> dentro de un <dialog> puede usar method="dialog". Enviar dicho formulario cierra el diálogo sin enviar una solicitud de red, y el valor del <button> de envío que se hizo clic se almacena en la propiedad DOM returnValue del diálogo. Esto facilita saber qué botón eligió el usuario.

<!DOCTYPE html>
<html>
  <head>
    <title>Dialog with a form</title>
  </head>
  <body>
    <button id="open">Delete file</button>
    <p id="result"></p>

    <dialog id="confirm">
      <form method="dialog">
        <p>Are you sure you want to delete this file?</p>
        <button value="cancel">Cancel</button>
        <button value="delete">Delete</button>
      </form>
    </dialog>

    <script>
      const dialog = document.getElementById("confirm");
      const result = document.getElementById("result");

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

      dialog.addEventListener("close", () => {
        // returnValue is the value of the button that submitted the form
        result.textContent = "You chose: " + dialog.returnValue;
      });
    </script>
  </body>
</html>

returnValue es una propiedad del elemento DOM, no un atributo HTML — la lees o estableces desde JavaScript. También puedes establecerla directamente al cerrar en código: dialog.close("delete").

Los eventos close y cancel

Un <dialog> dispara dos eventos que puedes escuchar:

EventoCuándo se dispara
closeSiempre que el diálogo se cierra — por close(), al enviar un formulario method="dialog", o por Esc. Lee returnValue aquí.
cancelCuando el usuario descarta un diálogo modal con la tecla Esc. Se dispara antes que close. Llama a event.preventDefault() para mantener el diálogo abierto.
<dialog id="editor">
  <p>Unsaved changes — Esc would normally close me.</p>
  <button onclick="this.closest('dialog').close()">Done</button>
</dialog>

<script>
  const dialog = document.getElementById("editor");

  dialog.addEventListener("cancel", (event) => {
    // Prevent Esc from discarding unsaved work
    event.preventDefault();
  });

  dialog.addEventListener("close", () => {
    console.log("Dialog closed");
  });
</script>

Para más información sobre cómo conectar estos listeners, consulta Eventos de JavaScript y el DOM HTML de JavaScript.

Accesibilidad

Un <dialog> tiene un role="dialog" implícito, pero aún necesitas darle un nombre accesible y gestionar el foco:

  • Nombra el diálogo. Apunta aria-labelledby al id del encabezado del diálogo para que los lectores de pantalla anuncien su título. Usa aria-describedby para una descripción más extensa.
  • Foco al abrir. showModal() mueve el foco al diálogo automáticamente — hacia el primer elemento enfocable, o un elemento que marques con el atributo autofocus. Prefiere colocar autofocus en un control seguro (como Cancelar) en lugar de uno destructivo.
  • Devolver el foco al cerrar. Cuando el diálogo se cierra, devuelve el foco al control que lo abrió para que los usuarios de teclado no queden al principio de la página. Con el <dialog> nativo, el navegador hace esto por ti; para obtener los mejores resultados, guarda y restaura el disparador tú mismo.
<button id="open">Edit profile</button>

<dialog id="profile" aria-labelledby="profile-title" aria-describedby="profile-desc">
  <h2 id="profile-title">Edit profile</h2>
  <p id="profile-desc">Update your display name, then save.</p>
  <form method="dialog">
    <input type="text" aria-label="Display name" autofocus>
    <button value="save">Save</button>
  </form>
</dialog>

<script>
  const openButton = document.getElementById("open");
  const dialog = document.getElementById("profile");

  openButton.addEventListener("click", () => dialog.showModal());

  // Explicitly return focus to the trigger when the dialog closes
  dialog.addEventListener("close", () => openButton.focus());
</script>

Consulta Consideraciones de accesibilidad en el DOM para más información sobre la creación de widgets interactivos accesibles.

Estilizar el diálogo y su backdrop

Puedes estilizar el propio cuadro de diálogo y, para los diálogos modales, el área detrás de él con el pseudo-elemento ::backdrop.

dialog {
  border: 1px solid #ccc;
  border-radius: 0.5em;
  padding: 1em;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}

/* Only rendered for dialogs opened with showModal() */
dialog::backdrop {
  background-color: rgba(0, 0, 0, 0.5);
}

Atributos

AtributoValorDescripción
openopenAtributo boolean que indica que el diálogo está actualmente visible. Normalmente lo establece show() / showModal() por ti.

La etiqueta <dialog> también admite los Atributos Globales y los Atributos de Evento.

Práctica

Práctica
¿Cuáles de las siguientes afirmaciones sobre el elemento HTML <dialog> son correctas?
¿Cuáles de las siguientes afirmaciones sobre el elemento HTML <dialog> son correctas?
Was this page helpful?