- O elemento
<dialog> do HTML cria caixas de diálogo modais e não modais nativas do navegador, e a modal coloca o restante da página em estado inert, bloqueando a interação
- As formas básicas de abrir e fechar são
showModal(), show() e close(), e também é possível fazer controle declarativo com form, Invoker Commands API e Popover API
- O atributo
closedby divide as formas pelas quais o usuário pode fechar a caixa de diálogo em any, closerequest e none, e o comportamento padrão muda conforme a forma como ela foi aberta
- Em acessibilidade, o foco inicial e um botão de fechar explícito são importantes, e caixas de diálogo abertas com
showModal() fornecem por padrão foco no primeiro elemento focalizável e fechamento com a tecla Esc
- A estilização usa
:modal, :open e ::backdrop, e animações exigem tratamento de transições discretas como display, overlay, @starting-style e transition-behavior: allow-discrete
Função do <dialog> e comportamento modal
- O elemento
<dialog> do HTML serve para criar caixas de diálogo modais e não modais
- A caixa de diálogo modal bloqueia a interação com outros elementos da UI e coloca o restante da página em estado
inert
- A caixa de diálogo não modal pode continuar interagindo com o restante da página mesmo quando está aberta
Estado de abertura e política de fechamento
<dialog> inclui atributos globais, mas o atributo tabindex não deve ser usado
- O próprio
<dialog> não é um elemento interativo e não recebe foco
- O conteúdo interno e o botão de fechar podem receber foco e ser usados normalmente
- O atributo
open indica que a caixa de diálogo está ativa e pode ser usada
- Sem o atributo
open, ela não fica visível para o usuário
- Para exibir a caixa de diálogo, é recomendado usar
.show() ou .showModal() em vez do atributo open
- Um
<dialog> aberto com o atributo open é não modal
- É possível alternar o estado aberto/fechado de uma caixa de diálogo não modal com o atributo
open, mas isso não é recomendado
- O atributo
closedby especifica de que forma o usuário pode fechar a caixa de diálogo
any: pode ser fechada das três formas
- Light dismiss, como clicar ou tocar fora da caixa de diálogo
- Ações da plataforma, como tecla Esc, voltar no mobile ou gesto de dispensar
- Formas definidas pelo desenvolvedor, como um botão que chama
HTMLDialogElement.close() ou um envio de form
closerequest: pode ser fechada por ações da plataforma e por formas definidas pelo desenvolvedor
none: só pode ser fechada por formas definidas pelo desenvolvedor
- Se não houver um valor
closedby válido, o comportamento será como closerequest quando aberta com showModal(), e como none nos demais casos
JavaScript e controle declarativo
- É possível controlar diretamente a exibição e o fechamento de
<dialog> com JavaScript
showModal(): exibe uma caixa de diálogo modal
show(): exibe uma caixa de diálogo não modal
close(): fecha a caixa de diálogo
- Também é possível fechar enviando um
form dentro de <dialog> com method="dialog"
- Caixas de diálogo modais também podem ser fechadas com a tecla Esc
- A Invoker Commands API permite abrir e fechar caixas de diálogo modais apenas com atributos no botão
- Define-se
commandfor e command em <button>
- Os comandos disponíveis para a caixa de diálogo são
"show-modal", "close" e "request-close"
Open dialog
This dialog was opened using an invoker command.
Close
- A Popover API permite abrir, fechar e alternar caixas de diálogo não modais de forma declarativa
- Adiciona-se o atributo
popover a <dialog> para transformá-lo em um popover
- Define-se
popovertarget e popovertargetaction em um botão ou elemento de entrada
- Como o popover dialog é não modal, ele pode ser fechado clicando fora
- Se nenhum valor for definido para
popover, o padrão "auto" será usado, ativando o light dismiss
popover="manual" desativa o light dismiss, exigindo outra forma de fechamento, como um botão
- Se
popovertargetaction for omitido, o padrão toggle será usado
Envio de formulário e tratamento de fechamento
- Todo
<dialog> precisa de um mecanismo de fechamento que funcione mesmo em dispositivos sem teclado físico
- Há várias formas de fechar
- Enviar um
form interno com method="dialog"
- Clicar fora da caixa de diálogo quando o light dismiss estiver ativado
- Pressionar Esc em caixas de diálogo onde isso é permitido
- Chamar
HTMLDialogElement.close()
formmethod="dialog" em <form> ou no botão de envio fecha a caixa de diálogo
- O estado dos controles do formulário é preservado, mas o formulário não é enviado
returnValue recebe o valor do botão ativado
- Em formulários com campos obrigatórios, o user agent impede que a caixa de diálogo seja fechada por envio normal até que os valores sejam fornecidos
- É possível contornar a validação do formulário usando
formnovalidate no botão de fechar
- Também é possível fechar com
dialog.close() em JavaScript
Foco e acessibilidade
- Ao abrir
<dialog> com showModal(), o foco é colocado por padrão no primeiro elemento focalizável aninhado
- Para definir a posição inicial de foco mais adequada em uma caixa de diálogo específica, pode-se usar o atributo
autofocus
- Se não houver um elemento para interação imediata, é recomendado colocar
autofocus no botão de fechar
- Se a posição inicial de foco for incerta, como em caixas de diálogo renderizadas dinamicamente, o próprio
<dialog> pode ser um ponto inicial apropriado
- A forma mais robusta de garantir que todos os usuários possam fechar a caixa de diálogo é incluir botões explícitos, como confirmar, cancelar ou fechar
- Caixas de diálogo abertas com
showModal() podem ser fechadas com Esc por padrão
- Caixas de diálogo não modais não são fechadas com Esc por padrão
- Usuários de teclado esperam conseguir fechar caixas de diálogo modais com Esc
- Se houver várias caixas de diálogo modais abertas, a tecla Esc deve fechar apenas a última exibida, e o navegador fornece esse comportamento ao usar
<dialog>
- O
<dialog> nativo oferece recursos de usabilidade e acessibilidade que precisariam ser reproduzidos manualmente em diálogos customizados feitos com outros elementos
- O navegador expõe
<dialog> de forma semelhante a um diálogo customizado com ARIA role="dialog"
- Um
<dialog> aberto com showModal() tem implicitamente aria-modal="true"
- Um
<dialog> exibido com show(), com o atributo open ou alterando o display padrão é exposto com aria-modal="false"
- Ao implementar uma caixa de diálogo modal, tudo fora de
<dialog> e seu conteúdo deve ser renderizado como inert, e o navegador fornece esse comportamento ao usar showModal()
Estilização em CSS e backdrop
<dialog> pode ser selecionado pelo nome do elemento como qualquer elemento comum
- Para estilização baseada em estado, podem ser usadas as pseudoclasses
:modal e :open
- O plano de fundo da caixa de diálogo modal pode ser estilizado com o pseudoelemento
::backdrop
- Ele é aplicado ao backdrop que aparece atrás de
<dialog> quando a caixa de diálogo é exibida com showModal()
- Pode ser usado para desfocar, escurecer ou cobrir o conteúdo ao fundo em estado inert
Padrões de uso vistos em exemplos
- O exemplo da Invoker Commands API abre a caixa de diálogo com um botão
command="show-modal" e fecha com um botão command="close"
- Pode ser aberta com o botão “Open dialog”
- Pode ser fechada com o botão “Close” ou com a tecla Esc
- O exemplo da Popover API abre e fecha uma caixa de diálogo não modal com
popover, popovertarget e popovertargetaction
- Pode ser fechada com o botão “Close”, com Esc ou selecionando fora da caixa de diálogo
- Ao usar
popover="manual", o light dismiss é desativado
- O exemplo com o atributo
open cria uma caixa de diálogo não modal apenas em HTML, já aberta ao carregar a página
- Pode ser fechada com o botão “OK” em
<form>
- Não há forma de reabri-la depois de fechada
- Para exibir uma caixa de diálogo não modal, prefere-se usar
HTMLDialogElement.show()
- O exemplo de caixa de diálogo modal usa
.showModal() para abrir e close() para fechar
- O
::backdrop é estilizado com um fundo em gradiente
- Quando a caixa de diálogo está aberta, a área fora dela entra em estado inert e não é possível interagir com o documento
- O exemplo de
returnValue trata o valor de retorno da caixa de diálogo com formulário e valores de botão
- O
returnValue padrão é uma string vazia ou o valor do botão que enviou o formulário interno da caixa de diálogo
- O botão “Confirm” passa o valor selecionado para
close() e o usa como valor de retorno
- Se a caixa for fechada com Esc,
returnValue não é atualizado e o evento close também não ocorre, então o texto de <output> não é atualizado
Animações de <dialog>
<dialog> oculto usa display: none, e <dialog> visível usa display: block
- Quando o estado de exibição muda, ele é adicionado ou removido da top layer e da árvore de acessibilidade
- Para animar
<dialog>, a propriedade display precisa ser animável
- Navegadores com suporte tratam
display como uma animação discreta
- Ao mudar de
none para block, a troca para block acontece no ponto 0%, então ele permanece visível durante toda a animação
- Ao mudar de
block para none, a troca para none acontece no ponto 100%, então ele permanece visível durante toda a animação
-
Animação com CSS transition
- Para animar
<dialog> com CSS transition, os seguintes recursos são necessários
@starting-style: fornece o valor inicial da transição toda vez que a caixa de diálogo é aberta
- Transição de
display: mantém a caixa de diálogo no valor visível de display durante a transição
- Transição de
overlay: atrasa a remoção da top layer até o fim da transição
transition-behavior: allow-discrete: ativa transições discretas para display e overlay, que normalmente não são animados
- Em navegadores sem suporte à pseudoclasse
:open, é possível estilizar o estado aberto com o seletor de atributo dialog[open]
- Como
<dialog> muda de display: none para display: block sempre que é exibido, cada transição de entrada vai de @starting-style para o estilo dialog:open
-
Animação com keyframes
- Animações CSS com keyframes têm restrições diferentes das transitions
- Não fornecem
@starting-style
- Incluem o valor de
display dentro do keyframe
- Não exigem ativação explícita de
allow-discrete
- Também não é necessário definir
overlay no keyframe
- O fade-out do backdrop não pode ser animado ao fechar, porque o backdrop é removido imediatamente do DOM quando a caixa de diálogo é fechada
Resumo técnico
- Categoria de conteúdo: flow content, sectioning root
- Conteúdo permitido: flow content
- As tags de abertura e fechamento são obrigatórias, sem omissão de tags
- Pai permitido: qualquer elemento que aceite flow content
- Papel ARIA implícito:
dialog
- Papel ARIA permitido:
alertdialog
- Interface DOM:
HTMLDialogElement
- Especificação: HTML # the-dialog-element
1 comentários
Comentários do Hacker News
Elementos HTML interativos incluem seletor de arquivos, seletor de cores, seletor de data/hora, controle deslizante numérico, opções sugeridas em campos de texto, resumo/detalhes expansíveis, FAQ e players de mídia com controles
Esses elementos são bons por serem leves e semânticos, e
<details>que compartilham o mesmonametambém podem ter o comportamento de fechar o item anterior quando um novo é abertoSeria bom se fossem melhor implementados
Uso
<dialog>desde 2019 e, mesmo que Firefox e Safari tenham levado alguns anos a mais para dar suporte, a qualidade do polyfill do Google era boa o suficiente para usá-lo em produção em um SaaS corporativo sem problemasO ponto mais decepcionante é que o elemento quase não tinha estilo. Era só uma borda preta grossa bem básica, com cantos retos, o que foi diferente da expectativa de que o navegador seguisse as convenções do sistema operacional para posicionamento de diálogos/janelas e fizesse com que parecesse um diálogo nativo do macOS, iOS ou Windows
Como o elemento aberto fica em uma camada superior separada, eu esperava que ele também pudesse sair para fora do viewport do navegador, mas essa expectativa também foi frustrada. Do ponto de vista do usuário, ninguém quer um pop-up ou diálogo modal imóvel que cubra completamente o conteúdo do documento
Os fornecedores de navegadores parecem não querer que se use
alert(),prompt()econfirm()porque eles bloqueiam o JavaScript/a thread principal, mas não ofereceram uma API substituta igualmente simples e eficaz, que não exija habilidade de design de UI. Em vez de tentar convencer que<dialog>é melhor em todos os casos, seria bom oferecer uma API de alert/prompt/confirm não bloqueante baseada em PromiseO fato de os novos recursos não se comportarem como a antiga família
alert()não é uma decepção acidental; também há vantagens nissoAinda assim, o estilo padrão de
<dialog>precisava de um pouco mais de atenção, e teria sido muito melhor se houvesse ao menos um estilo padrão que combinasse com o estilo nativo do navegador, mesmo sem parecer e se comportar 100% como um diálogo de sistema fora do DOMPara web apps com mais permissões do que páginas comuns, como PWAs, essa direção poderia ser mais bem aceita, como recursos separados de estilização de janelas ou interação com o sistema
<dialog>parecer uma janela nativa do navegador, isso pode borrar a line of deathFica mais fácil para um site malicioso exibir um pop-up de “atualização do navegador” no meio da página e redirecionar para uma página de download do Chrome convincente, fazendo o usuário baixar um executável adulterado
https://textslashplain.com/2017/01/14/the-line-of-death/
Usuários mantêm várias abas abertas, e outra aba pode conter informações necessárias para concluir o diálogo
Também é preciso ter muito cuidado com quanto controle visual será dado ao diálogo em si. Especialmente se ele puder parecer o sistema operacional, já há muita gente que cai em alertas falsos de vírus no navegador; um diálogo que pareça real e exija interação pode aumentar muito a taxa de sucesso dos ataques
A maioria das empresas em que trabalhei tinha algum grau de linguagem de design consistente, e, independentemente do que digam os defensores de HTML/CSS puro, usar componentes embutidos do navegador sem alterações quase sempre parecia amador e deslocado
Se esse problema não for resolvido, será difícil haver adoção ampla
alert(),prompt()econfirm()funcionarem comoawait Prompts.alert("This is an alert message!");,await Prompts.confirm(...),await Prompts.prompt(...)Demo: https://tools.simonwillison.net/prompts-js
O código foi escrito pelo o1: https://chatgpt.com/share/67539c28-4df0-8006-b021-4f468e011f...
Escrevi um post chamado “The HTML dialog element API is a mess”: https://lapcatsoftware.com/articles/2024/2/1.html
Ainda assim, o valor está em serem padrões. A Web é ótima porque funciona em qualquer lugar, e inevitavelmente acaba sendo bagunçada
Nesse contexto, vejo o
<dialog>como um sucesso, especialmente em ferramentas internas de administração. Não quero me preocupar com a última moda de front-end; só quero economizar espaço na tela e exibir conteúdo como uma sobreposição modal sobre a visualização principalPara abrir uma caixa de diálogo é necessária uma entrada do usuário, e uma caixa de diálogo modal bloqueia a entrada do usuário; então, para isso acontecer, uma entrada dentro da própria caixa de diálogo teria de abrir a mesma caixa de diálogo de novo
Se você abrir a caixa de diálogo por uma operação assíncrona, precisa acompanhar o que está aberto e fechado, e algo parecido acontece até em ambientes como Qt
<dialog>fosse uma versão reforçada de await confirm/prompt, customizável, que não mostrasse alertas diferentes em cada navegador quando aberta várias vezesMas, na prática, ele está mais para uma
divglorificadaA Compression Streams API não era ruim, mas era uma API bem pequena
Pelo padrão, parece que o Google não lida bem com experiência do usuário e experiência do desenvolvedor
Além disso, fui procurar a posição dos padrões e vi que ambos dão suporte a URLPattern
Independentemente da implementação, acho que foi um passo na direção certa
Também há uma proposta em andamento que parece uma versão aprimorada do
<select>: https://open-ui.org/components/combobox.explainer/Para notificações toast, a Popover API já chegou aos navegadores: https://mdn.github.io/dom-examples/popover-api/
Também existe uma proposta de popover hint para tooltips: https://open-ui.org/components/popover-hint.research.explain...
Gosto do elemento
<dialog>, especialmente das considerações de acessibilidade incorporadas e padronizadasEstou ansioso pelo dia em que poderei usá-lo sem polyfill quando Safari abaixo da versão 15.4 sair dos critérios de suporte
Mas minha maior reclamação é a dependência de JavaScript. Quase todos os visitantes de um site vêm com JavaScript ativado, mas também sinto certa satisfação em fazer com que isso não seja necessário
Não sei por que CSS ou um botão de destino não podem controlar o estado aberto da caixa de diálogo, e gostaria de saber se eu estiver errado
popover=""Ele pode ser facilmente usado como alvo por um botão, não precisa de JavaScript no lado do cliente, e também pode ser fechado ao clicar fora. Esse é um comportamento que o
<dialog>não tem por padrãoNão tenho certeza quanto à acessibilidade, mas imagino que não seja pior do que as abordagens antigas com labels/checkboxes/elementos de formulário ocultos; é muito mais simples e parece menos uma gambiarra
https://css-tricks.com/invoker-commands-additional-ways-to-w...
Seria bom se HTML desse suporte ao conceito de uma tag como
<page>, capaz de definir várias páginas dentro de um único arquivo e mostrar uma por vezSem parecer uma caixa de diálogo, e permitindo que cada PAGE puxasse seções comuns, como cabeçalho, barra lateral e rodapé dentro da mesma página, conforme o estado selecionado
Hoje dá para fazer isso escondendo e mostrando
divs, mas dar suporte a essa abordagem com uma tag HTML específica poderia ajudarTentei usar isso hoje e encontrei um problema que não consegui contornar
Quando há um formulário dentro da caixa de diálogo, se você pressiona Enter com o foco em um campo de entrada, ou pressiona Space com o foco no botão de envio, a caixa de diálogo é fechada junto com o envio do formulário
Não encontrei uma forma limpa de impedir isso. Normalmente um formulário recarrega a página, então talvez não seja um problema comum, mas aqui eu estava usando htmx
Uso um editor em que um formulário dentro de
<dialog>atualiza um iframe, e apenas o iframe de destino é recarregado; a caixa de diálogo não fechaTambém não consegui reproduzir um caso em que a caixa de diálogo fechasse ao pressionar Enter. Deveria ser igual a
submit, esubmittambém não fecha a caixa de diálogoA propósito, aprendi ontem que botões podem fechar uma caixa de diálogo
preventDefaultestopPropagation?Enquanto procurava um gerenciador de consentimento de cookies para colocar numa build recém-otimizada, não gostei de ver que as opções open source passavam de 100 KB, então fiz o meu próprio: https://github.com/replete/biscuitman
Para deixá-lo o menor possível, dependi do
<dialog>, e com apenas algumas regras CSS ele funciona de forma nativa mesmo sem estiloNo fim, também acabei escrevendo uma ferramenta de build que compila até para IE11 e versões realmente antigas de navegadores
O
<dialog>funciona bem no geral e, em navegadores antigos, exige alguns truques de CSS, mas não é difícil de lidar. É uma boa adição à plataforma Web, mas, depois de 20 anos fazendo isso, não quero ter de recriar controles customizados de seleção múltipla a cada poucos anos. Controles nativos são bonsO comportamento comum de fechar na maioria dos exemplos não funciona no Firefox para Android
autofocusdo botão de fechar não funcionouNo fim, toda vez que chamava
show(), adicionei uma linha como$('#thatModal *[autofocus]').focus()Segundo a MDN, por padrão deveria funcionar como esperado
Parece que só os listeners adicionados via JavaScript funcionam corretamente