Los diálogos modales modernos suelen admitir la opción de "descartar con un toque", lo que permite que los usuarios cierren un diálogo haciendo clic o presionando el fondo (el área fuera del diálogo). El atributo closedby proporciona una forma declarativa de habilitar este comportamiento sin JavaScript personalizado.
Implementación
Para habilitar el cierre con un clic, haz lo siguiente:
- Agrega
closedby="any"al elemento<dialog>. - Abre el diálogo con
dialog.showModal().
Valores del atributo
any: Habilita el cierre rápido (hacer clic en el fondo), las "solicitudes de cierre" (la teclaEsc) y los mecanismos para desarrolladores (p.ej.,dialog.close()).closerequest: Habilita solo las "solicitudes de cierre" y los mecanismos para desarrolladores. Este es el comportamiento predeterminado de los diálogos modales.none: Solo los mecanismos del desarrollador pueden cerrar el diálogo.
Aplica diseño al fondo
Cuando se abre un diálogo como modal con showModal(), el navegador genera un pseudoelemento ::backdrop. Este fondo cubre todo el viewport y se ubica directamente detrás del diálogo.
/* Style the backdrop to indicate the dialog is modal */
dialog::backdrop {
background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(2px); /* Optional: add blur for modern browsers */
}
Ejemplo
<!-- MANDATORY: Use closedby="any" to enable light-dismiss behavior -->
<dialog id="myDialog" closedby="any" aria-labelledby="dialogTitle">
<form method="dialog">
<h2 id="dialogTitle">Feedback</h2>
<p>Click outside this box or press Esc to dismiss.</p>
<button type="submit">Close</button>
</form>
</dialog>
<button onclick="document.getElementById('myDialog').showModal()">Open Dialog</button>
Restricciones y accesibilidad
- OBLIGATORIO: Usa
closedby="any"para habilitar el cierre rápido de forma declarativa. - OBLIGATORIO: Abre siempre los diálogos modales con
showModal(). Esto garantiza que el diálogo esté en la capa superior, que el enfoque quede atrapado y que se controle la teclaEsc. - SÍ: Usa
aria-labelledbyoaria-labelpara proporcionar un nombre de accesibilidad para el diálogo. - NO uses
closedbypara diálogos no modales (abiertos conshow()), ya que no tienen un fondo y no activarán el cierre con un toque fuera del diálogo. - NO uses el evento
clickpara la lógica crítica que debería ocurrir antes del cierre. En su lugar, escucha los eventoscloseocancel.
Estrategias de resguardo
OBLIGATORIO: En el caso de los navegadores que aún no admiten closedby, debes implementar un resguardo para el cierre por pérdida de foco. Para ello, verifica si se produjo un clic fuera de los límites del contenido del diálogo con la siguiente secuencia de comandos:
const dialog = document.querySelector('dialog');
// Fallback for browsers without closedby support
if (!('closedBy' in HTMLDialogElement.prototype)) {
dialog.addEventListener('click', (event) => {
// 1. When clicking the backdrop, the event target is the dialog element itself.
// Ignore clicks where the target is a child element inside the dialog.
if (event.target !== dialog) return;
// 2. Check if the click coordinates fall within the dialog's content box.
// This distinguishes between a click on the backdrop vs a click on the dialog's background/padding.
const rect = dialog.getBoundingClientRect();
const isDialogContent = (
rect.top <= event.clientY &&
event.clientY <= rect.top + rect.height &&
rect.left <= event.clientX &&
event.clientX <= rect.left + rect.width
);
if (isDialogContent) return;
// 3. Since the click was outside the content area (on the backdrop), manually close the dialog.
dialog.close();
});
}