O elemento <dialog> sempre fechava com Esc de um jeito meio "tudo ou nada": funcionava, mas não dava pra controlar quais gestos do usuário deveriam realmente fechar o modal. O atributo closedby resolve isso, permitindo declarar explicitamente se o dialog fecha só por script, por Esc, ou também por clique fora dele (o chamado "light dismiss").
Os três valores possíveis
closedby aceita três valores:
none— o dialog só fecha viaclose()ourequestClose()chamados em JavaScript. Nem Esc nem clique fora fazem efeito.closerequest— além do script, a tecla Esc (ou o gesto de "voltar" em mobile/TV) fecha o dialog.any— tudo isso, mais clique fora do dialog (no::backdrop), fechando o modal com um clique no restante da página.
Light dismiss sem JavaScript
Antes do closedby, fechar um dialog clicando fora dele exigia um listener de clique comparando event.target com o próprio dialog. Agora é declarativo:
<dialog id="modal" closedby="any">
<p>Clique fora para fechar.</p>
<button id="fechar">Fechar</button>
</dialog>
document.getElementById('modal').showModal();
Com closedby="any", clicar em qualquer área fora do conteúdo do dialog fecha o modal automaticamente, sem uma linha de JavaScript para isso.
Travando o fechamento com closedby="none"
Para fluxos onde o usuário precisa tomar uma decisão explícita (aceitar termos, confirmar uma exclusão), dá pra bloquear Esc e clique fora:
<dialog id="confirmar" closedby="none">
<p>Esta ação não pode ser desfeita. Deseja continuar?</p>
<button id="cancelar">Cancelar</button>
<button id="confirmar-btn">Confirmar</button>
</dialog>
Aqui o dialog só fecha quando um dos botões chamar close() explicitamente — Esc e clique fora não têm efeito nenhum.
O valor padrão (auto)
Quando o atributo está ausente, o comportamento depende de como o dialog foi aberto: showModal() se comporta como closerequest (Esc fecha, clique fora não), enquanto um dialog não-modal aberto com show() se comporta como none. Ou seja, o comportamento "clássico" do <dialog> continua o mesmo — closedby só existe para os casos em que você quer outra coisa.
requestClose() e o evento cancel
Junto com closedby veio o método requestClose(), que difere de close() por disparar o evento cancel antes de fechar — o mesmo evento disparado quando o usuário aperta Esc. Isso permite interceptar qualquer tentativa de fechamento, seja por script ou por gesto do usuário, no mesmo lugar:
const modal = document.getElementById('modal');
modal.addEventListener('cancel', (event) => {
if (formTemAlteracoesNaoSalvas()) {
event.preventDefault();
alert('Salve ou descarte as alterações antes de fechar.');
}
});
// dispara o mesmo evento 'cancel' que o Esc dispararia
document.getElementById('fechar-programaticamente').addEventListener('click', () => {
modal.requestClose();
});
Chamar event.preventDefault() no cancel cancela o fechamento — o dialog permanece aberto tanto se o usuário apertou Esc quanto se algum código chamou requestClose().
Quando usar
closedby="any"em menus, painéis de filtro e popovers disfarçados de dialog, onde clicar fora é o comportamento esperado.closedby="none"em confirmações críticas e formulários com dados não salvos.requestClose()+ eventocancelpara validar ou avisar antes de fechar, unificando Esc e fechamento programático numa única lógica.
Compatibilidade
O closedby e o requestClose() chegaram no Chrome 134 e ainda não têm suporte no Firefox e no Safari. Como o navegador ignora um atributo que não reconhece, o dialog continua funcionando normalmente nos demais navegadores — só sem o comportamento de light dismiss declarativo, caindo no padrão closerequest. Vale usar com um fallback (um listener de clique no backdrop) enquanto o suporte não se generaliza.
Conclusão
O closedby transforma um comportamento que antes exigia JavaScript manual e cuidado com detalhes de acessibilidade em uma única palavra declarativa no HTML. Combinado com requestClose() e o evento cancel, dá controle fino sobre como e quando um dialog pode ser fechado, sem duplicar lógica entre Esc, clique fora e botões.
0 Comentário
Deixe seu Comentário!
Dúvida, sugestão ou só um "valeu!" — tudo é bem-vindo por aqui.