closedby: Controlando Como um Dialog Pode Ser Fechado

Como o atributo closedby do elemento dialog permite declarar se ele fecha por Esc, por clique fora (light dismiss) ou só por script, e como o método requestClose() unifica isso com o evento cancel.

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:

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

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.

caniuse

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.

Tecnologia:
html

Deixe seu Comentário!

Dúvida, sugestão ou só um "valeu!" — tudo é bem-vindo por aqui.

Comentário
Nome
Email
lock Seu e-mail não será publicado.