Dispensar uma caixa de diálogo com um toque

Recursos da Web: <dialog closedby>

As caixas de diálogo modais modernas geralmente oferecem suporte ao "fechamento rápido", permitindo que os usuários fechem uma caixa de diálogo clicando ou tocando no plano de fundo (a área fora da caixa de diálogo). O atributo closedby oferece uma maneira declarativa de ativar esse comportamento sem JavaScript personalizado.

Implementação

Para ativar a dispensa leve:

  1. Adicione closedby="any" ao elemento <dialog>.
  2. Abra a caixa de diálogo usando dialog.showModal().

Valores de atributo

  • any: ativa o fechamento rápido (clicar no plano de fundo), as "solicitações de fechamento" (a tecla Esc) e os mecanismos para desenvolvedores (por exemplo, dialog.close()).
  • closerequest: ativa apenas as "solicitações de fechamento" e os mecanismos para desenvolvedores. Esse é o padrão para caixas de diálogo modais.
  • none: somente os mecanismos do desenvolvedor podem fechar a caixa de diálogo.

Como estilizar o pano de fundo

Quando uma caixa de diálogo é aberta como um modal usando showModal(), o navegador gera um pseudoelemento ::backdrop. Esse plano de fundo cobre toda a janela de visualização e fica diretamente atrás da caixa de 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 */
}

Exemplo

<!-- 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>

Restrições e acessibilidade

  • OBRIGATÓRIO: use closedby="any" para ativar a dispensa leve de forma declarativa.
  • OBRIGATÓRIO: sempre abra caixas de diálogo modais com showModal(). Isso garante que a caixa de diálogo esteja na camada superior, que o foco seja mantido e que a tecla Esc seja processada.
  • RECOMENDADO: use aria-labelledby ou aria-label para fornecer um nome acessível para a caixa de diálogo.
  • NÃO: use closedby para caixas de diálogo não modais (abertas com show()), porque elas não têm um plano de fundo e não acionam o fechamento ao tocar fora.
  • NÃO: use o evento click para uma lógica crítica que precisa acontecer antes do fechamento. Em vez disso, detecte os eventos close ou cancel.

Estratégias de fallback

OBRIGATÓRIO: para navegadores que ainda não oferecem suporte a closedby, você precisa implementar um substituto para o fechamento leve, verificando se um clique ocorreu fora dos limites do conteúdo da caixa de diálogo usando o seguinte script:

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();
  });
}