- L’élément HTML
<dialog> permet de créer des boîtes de dialogue modales et non modales natives du navigateur ; en mode modal, le reste de la page passe à l’état inert, ce qui bloque toute interaction
- Les méthodes d’ouverture et de fermeture de base sont
showModal(), show() et close() ; un contrôle déclaratif est aussi possible via <form method="dialog">, l’Invoker Commands API et la Popover API
- L’attribut
closedby répartit les chemins de fermeture accessibles à l’utilisateur en any, closerequest et none, avec un comportement par défaut qui varie selon la manière dont la boîte de dialogue a été ouverte
- Côté accessibilité, le focus initial et un bouton de fermeture explicite sont importants ; une boîte de dialogue ouverte avec
showModal() place par défaut le focus sur le premier élément focalisable et permet la fermeture avec la touche Échap
- Le style s’appuie sur
:modal, :open et ::backdrop, et les animations nécessitent la gestion de transitions discrètes avec display, overlay, @starting-style et transition-behavior: allow-discrete
Rôle de <dialog> et comportement modal
- L’élément HTML
<dialog> sert à créer des boîtes de dialogue modales et non modales
- Une boîte de dialogue modale bloque l’interaction avec les autres éléments de l’interface et place le reste de la page dans l’état
inert
- Une boîte de dialogue non modale permet de continuer à interagir avec le reste de la page même lorsqu’elle est ouverte
État d’ouverture et politique de fermeture
<dialog> prend en charge les attributs globaux, mais l’attribut tabindex ne doit pas être utilisé
<dialog> lui-même n’est pas un élément interactif et ne reçoit pas le focus
- Son contenu interne et son bouton de fermeture peuvent recevoir le focus et être utilisés
- L’attribut
open indique qu’une boîte de dialogue est active et interactive
- Sans l’attribut
open, elle n’est pas visible pour l’utilisateur
- Pour afficher une boîte de dialogue, il est recommandé d’utiliser
.show() ou .showModal() plutôt que l’attribut open
- Un
<dialog> ouvert via l’attribut open est non modal
- Il est possible de changer l’état ouvert/fermé d’une boîte de dialogue non modale en basculant l’attribut
open, mais ce n’est pas recommandé
- L’attribut
closedby précise de quelles façons l’utilisateur peut fermer la boîte de dialogue
any : fermeture possible par les trois méthodes
- light dismiss via un clic ou un appui en dehors de la boîte de dialogue
- action propre à la plateforme, comme la touche Échap, le retour arrière sur mobile ou un geste de fermeture
- méthode définie par le développeur, comme un bouton appelant
HTMLDialogElement.close() ou l’envoi d’un <form>
closerequest : fermeture possible par action propre à la plateforme et par méthode définie par le développeur
none : fermeture possible uniquement par une méthode définie par le développeur
- En l’absence de valeur
closedby valide, le comportement est celui de closerequest si l’ouverture s’est faite avec showModal(), sinon celui de none
JavaScript et contrôle déclaratif
- Il est possible de contrôler directement l’affichage et la fermeture de
<dialog> en JavaScript
showModal() : affiche une boîte de dialogue modale
show() : affiche une boîte de dialogue non modale
close() : ferme la boîte de dialogue
- On peut aussi la fermer en soumettant un
<form> à l’intérieur de <dialog> avec method="dialog"
- Une boîte de dialogue modale peut aussi être fermée avec la touche Échap
- L’Invoker Commands API permet d’ouvrir et de fermer une boîte de dialogue modale via les seuls attributs d’un bouton
- On définit les attributs
commandfor et command sur un <button>
- Les commandes disponibles pour une boîte de dialogue sont
"show-modal", "close" et "request-close"
Open dialog
This dialog was opened using an invoker command.
Close
- La Popover API permet d’ouvrir, fermer et basculer déclarativement une boîte de dialogue non modale
- On ajoute l’attribut
popover à <dialog> pour en faire un popover
- On définit
popovertarget et popovertargetaction sur un bouton ou un champ de saisie
- Comme une boîte de dialogue popover est non modale, on peut la fermer en cliquant à l’extérieur
- Si aucune valeur n’est donnée à
popover, la valeur par défaut "auto" est utilisée et active le light dismiss
popover="manual" désactive le light dismiss et nécessite un autre mécanisme, comme un bouton de fermeture
- Si
popovertargetaction est omis, la valeur par défaut toggle est utilisée
Envoi de formulaire et gestion de fermeture
- Toute boîte de dialogue
<dialog> a besoin d’un mécanisme de fermeture, y compris sur les appareils sans clavier physique
- Plusieurs méthodes de fermeture sont possibles
- soumettre un
<form> interne avec method="dialog"
- cliquer en dehors de la boîte de dialogue lorsque le light dismiss est activé
- appuyer sur Échap pour les boîtes de dialogue qui l’autorisent
- appeler
HTMLDialogElement.close()
method="dialog" sur un <form> ou formmethod="dialog" sur un bouton d’envoi ferme la boîte de dialogue
- L’état des contrôles du formulaire est conservé, mais le formulaire n’est pas envoyé
returnValue reçoit la valeur du bouton activé
- Dans un formulaire avec des champs obligatoires, l’agent utilisateur empêche qu’une soumission classique ferme la boîte de dialogue tant que les valeurs requises ne sont pas fournies
- On peut contourner la validation du formulaire avec
formnovalidate sur le bouton de fermeture
- Il est aussi possible de fermer la boîte de dialogue en appelant
dialog.close() en JavaScript
Focus et accessibilité
- Lorsqu’on ouvre
<dialog> avec showModal(), le focus est placé par défaut sur le premier élément focalisable imbriqué
- Pour définir l’emplacement de focus initial le plus adapté dans une boîte de dialogue donnée, on peut utiliser l’attribut
autofocus
- S’il n’y a pas d’élément avec lequel interagir immédiatement, il est recommandé de placer
autofocus sur le bouton de fermeture
- Si la position de focus initiale est incertaine, comme dans une boîte de dialogue rendue dynamiquement,
<dialog> lui-même peut constituer une position initiale appropriée
- La solution la plus robuste consiste à inclure des boutons explicites tels que confirmer, annuler ou fermer afin que tous les utilisateurs puissent la fermer
- Une boîte de dialogue ouverte via
showModal() se ferme par défaut avec la touche Échap
- Une boîte de dialogue non modale ne se ferme pas par défaut avec la touche Échap
- Les utilisateurs clavier s’attendent à pouvoir fermer une boîte de dialogue modale avec Échap
- Si plusieurs boîtes de dialogue modales sont ouvertes, la touche Échap ne doit fermer que la dernière affichée, et le navigateur fournit ce comportement avec
<dialog>
- Le composant natif
<dialog> fournit des fonctions d’utilisabilité et d’accessibilité qu’il faut reproduire manuellement dans une boîte de dialogue personnalisée construite avec d’autres éléments
- Le navigateur expose
<dialog> de manière similaire à une boîte de dialogue personnalisée utilisant le rôle ARIA role="dialog"
- Un
<dialog> ouvert avec showModal() possède implicitement aria-modal="true"
- Un
<dialog> affiché via show(), l’attribut open ou une modification du display par défaut est exposé avec aria-modal="false"
- Lorsqu’on implémente une boîte de dialogue modale, tout ce qui n’est ni
<dialog> ni son contenu doit être rendu inert, ce que le navigateur gère automatiquement avec showModal()
Style CSS et arrière-plan
<dialog> peut être sélectionné par son nom d’élément comme n’importe quel élément standard
- Pour un style basé sur l’état, on peut utiliser les pseudo-classes
:modal et :open
- L’arrière-plan d’une boîte de dialogue modale peut être stylé avec le pseudo-élément
::backdrop
- Il s’applique au backdrop qui apparaît derrière
<dialog> lorsqu’il est affiché avec showModal()
- Il peut servir à flouter, assombrir ou masquer le contenu en arrière-plan devenu inert
Exemples de patterns d’utilisation
- L’exemple avec l’Invoker Commands API ouvre la boîte de dialogue avec un bouton
command="show-modal" et la ferme avec un bouton command="close"
- Elle peut être ouverte avec le bouton « Open dialog »
- Elle peut être fermée avec le bouton « Close » ou la touche Échap
- L’exemple avec la Popover API ouvre et ferme une boîte de dialogue non modale avec
popover, popovertarget et popovertargetaction
- Elle peut être fermée avec le bouton « Close », la touche Échap ou une sélection en dehors de la boîte de dialogue
- Avec
popover="manual", le light dismiss est désactivé
- L’exemple avec l’attribut
open crée une boîte de dialogue non modale en HTML pur déjà ouverte au chargement de la page
- Elle peut être fermée avec le bouton « OK » du
<form>
- Aucun mécanisme n’est prévu pour la rouvrir ensuite
- Pour afficher une boîte de dialogue non modale, l’usage de
HTMLDialogElement.show() est préférable
- L’exemple de boîte de dialogue modale l’ouvre avec
.showModal() et la ferme avec close()
- Le fond en dégradé est stylé avec
::backdrop
- Une fois la boîte de dialogue ouverte, tout ce qui est en dehors devient inert et il n’est plus possible d’interagir avec le document
- L’exemple
returnValue montre comment gérer la valeur de retour de la boîte de dialogue avec un formulaire et les valeurs des boutons
- La valeur
returnValue par défaut est une chaîne vide ou la valeur du bouton qui a soumis le formulaire à l’intérieur de la boîte de dialogue
- Le bouton « Confirm » transmet la valeur choisie à
close() pour l’utiliser comme valeur de retour
- Si la boîte de dialogue est fermée avec la touche Échap,
returnValue n’est pas mis à jour et l’événement close n’est pas déclenché, donc le texte du <output> n’est pas actualisé
Animations de <dialog>
- Un
<dialog> masqué a display: none, et un <dialog> affiché a display: block
- Lors du changement d’état d’affichage, il est ajouté ou retiré de la top layer et de l’arbre d’accessibilité
- Pour animer
<dialog>, il faut que la propriété display puisse être animée
- Les navigateurs compatibles traitent
display comme une animation discrète
- Lors du passage de
none à block, la bascule vers block se fait à 0 %, ce qui rend la boîte de dialogue visible pendant toute l’animation
- Lors du passage de
block à none, la bascule vers none se fait à 100 %, ce qui la maintient visible pendant toute l’animation
-
Animation avec les transitions CSS
- Pour animer
<dialog> avec des transitions CSS, plusieurs fonctions sont nécessaires
@starting-style : fournit les valeurs de départ de la transition à chaque ouverture de la boîte de dialogue
- transition de
display : maintient la boîte de dialogue sur une valeur display visible pendant la transition
- transition de
overlay : retarde le retrait de la top layer jusqu’à la fin de la transition
transition-behavior: allow-discrete : active les transitions discrètes de display et overlay, qui ne sont normalement pas animées
- Dans les navigateurs qui ne prennent pas en charge la pseudo-classe
:open, on peut styliser l’état ouvert avec le sélecteur d’attribut dialog[open]
- Comme
<dialog> passe de display: none à display: block à chaque affichage, chaque transition d’entrée s’effectue de @starting-style vers le style dialog:open
-
Animation par keyframes
- Les animations CSS par keyframes ont des contraintes différentes de celles des transitions
- Elles ne fournissent pas
@starting-style
- La valeur
display est incluse dans les keyframes
- Il n’est pas nécessaire d’activer explicitement
allow-discrete
- Il n’est pas non plus nécessaire de définir
overlay dans les keyframes
- Le fade-out du backdrop ne peut pas être animé à la fermeture, car le backdrop est immédiatement retiré du DOM lorsque la boîte de dialogue se ferme
Résumé technique
- Catégories de contenu : flow content, sectioning root
- Contenu autorisé : flow content
- Les balises d’ouverture et de fermeture sont toutes deux obligatoires, sans omission possible
- Parent autorisé : tout élément acceptant du flow content
- Rôle ARIA implicite :
dialog
- Rôle ARIA autorisé :
alertdialog
- Interface DOM :
HTMLDialogElement
- Spécification : HTML # the-dialog-element
1 commentaires
Avis sur Hacker News
Parmi les éléments HTML interactifs, on trouve les sélecteurs de fichiers, les sélecteurs de couleur, les sélecteurs de date/heure, les curseurs numériques, les options suggérées dans les champs texte, les résumés/détails dépliables, les FAQ, ou encore les lecteurs multimédias avec contrôles.
Ces éléments sont appréciables parce qu’ils sont légers et sémantiques, et des éléments
<details>partageant le mêmenamepeuvent aussi permettre de fermer l’élément précédent quand on en ouvre un autre.Ce serait bien qu’ils soient mieux implémentés.
J’utilise
<dialog>depuis 2019, et même si Firefox et Safari ne l’ont pas pris en charge pendant encore quelques années, le polyfill de Google était de bonne qualité, si bien que nous l’avons utilisé en production dans un SaaS professionnel sans problème.Le plus regrettable, c’est que l’élément n’était quasiment pas stylisé. Il n’avait qu’une bordure noire épaisse très basique, en pixels, et des coins anguleux, ce qui était loin de ce que j’espérais : que le navigateur suive les conventions du système d’exploitation pour les boîtes de dialogue et la disposition des fenêtres, afin qu’elles ressemblent à de vraies boîtes de dialogue macOS, iOS ou Windows.
Comme l’élément ouvert se trouve dans une couche supérieure séparée, j’espérais aussi qu’il puisse sortir du viewport du navigateur, mais cette attente a également été déçue. Du point de vue de l’utilisateur, on ne souhaite pas une popup immobile ou une boîte de dialogue modale qui masque entièrement le contenu du document.
Les éditeurs de navigateurs semblent vouloir nous dissuader d’utiliser
alert(),prompt()etconfirm()parce qu’ils bloquent JavaScript/le thread principal, mais ils n’ont pas proposé d’API de remplacement aussi simples, efficaces et ne nécessitant aucune compétence en design d’interface. Plutôt que d’essayer de convaincre que<dialog>est meilleur dans tous les cas, ce serait bien de fournir une API alert/prompt/confirm non bloquante basée sur des Promise.Le fait que les nouvelles fonctionnalités ne se comportent plus comme l’ancienne famille
alert()n’est pas seulement une déception accidentelle, cela a aussi des avantages.Cela dit, le style par défaut de
<dialog>aurait mérité un peu plus d’attention, et même sans ressembler ni se comporter à 100 % comme une boîte de dialogue système hors du DOM, un style par défaut cohérent avec le style natif du navigateur aurait déjà été bien meilleur.Pour les applications web disposant de plus de permissions qu’une page ordinaire, comme les PWA, cette direction pourrait être mieux acceptée, par exemple avec des fonctionnalités séparées pour le style des fenêtres ou l’interaction avec le système.
<dialog>ressemble à une fenêtre native du navigateur, cela pourrait brouiller la line of death.Il deviendrait facile pour un site malveillant d’afficher au milieu de la page une popup « mise à jour du navigateur », puis de rediriger vers une page de téléchargement Chrome crédible afin de faire télécharger un exécutable modifié.
https://textslashplain.com/2017/01/14/the-line-of-death/
Les utilisateurs ont plusieurs onglets ouverts, et l’information nécessaire pour terminer l’action dans la boîte de dialogue peut se trouver dans un autre onglet.
Il faut aussi être très prudent quant au degré de contrôle visuel accordé à une vraie boîte de dialogue. En particulier, si on lui donne l’apparence du système d’exploitation, alors qu’aujourd’hui déjà beaucoup de gens se font avoir par de fausses alertes de virus du navigateur, une boîte de dialogue qui semble authentique et avec laquelle il faut impérativement interagir pourrait fortement augmenter le taux de réussite des attaques.
La plupart des entreprises où j’ai travaillé avaient un langage de design plus ou moins cohérent, et quoi qu’en disent les partisans du HTML/CSS pur, utiliser tels quels les composants intégrés au navigateur donnait le plus souvent un rendu maladroit, amateur.
Tant que ce problème ne sera pas résolu, il sera difficile qu’ils soient largement adoptés.
alert(),prompt()etconfirm()sous la formeawait Prompts.alert("This is an alert message!");,await Prompts.confirm(...),await Prompts.prompt(...).Démo : https://tools.simonwillison.net/prompts-js
Le code a été écrit par o1 : https://chatgpt.com/share/67539c28-4df0-8006-b021-4f468e011f...
J’ai écrit un billet intitulé « The HTML dialog element API is a mess » : https://lapcatsoftware.com/articles/2024/2/1.html
Leur valeur tient malgré tout au fait qu’ils sont des standards. Le Web est formidable parce qu’il fonctionne partout, et il est forcément désordonné.
Dans ce contexte, je considère que
<dialog>est une réussite, surtout pour les outils d’administration internes. On ne veut pas se soucier de la dernière mode front-end ; on veut simplement économiser de l’espace à l’écran en affichant du contenu sous forme de surcouche modale au-dessus de la vue principale.Ouvrir une boîte de dialogue nécessite une entrée utilisateur, et une boîte de dialogue modale bloque les entrées utilisateur ; pour que cela se produise, il faudrait donc qu’une interaction à l’intérieur de la boîte de dialogue rouvre cette même boîte.
Si vous ouvrez une boîte de dialogue via une opération asynchrone, vous devez suivre ce qui est ouvert ou fermé ; des situations similaires arrivent aussi dans des environnements comme Qt.
<dialog>devienne une version renforcée de await confirm/prompt, personnalisable, sans avertissements différents selon les navigateurs quand on l’ouvre plusieurs fois.En réalité, cela ressemble plutôt à un
divenjolivé.La Compression Streams API n’était pas mauvaise, mais c’était une toute petite API.
À voir la tendance, Google semble mal gérer l’expérience utilisateur comme l’expérience développeur.
Cela dit, en cherchant les positions des standards, j’ai constaté que les deux prennent en charge URLPattern.
Indépendamment de l’implémentation, je pense que c’était un pas dans la bonne direction.
Une proposition ressemblant à un
<select>amélioré est aussi en cours : https://open-ui.org/components/combobox.explainer/Pour les notifications toast, la Popover API est déjà arrivée dans les navigateurs : https://mdn.github.io/dom-examples/popover-api/
Il existe aussi une proposition de popover hint pour les infobulles : https://open-ui.org/components/popover-hint.research.explain...
J’aime l’élément
<dialog>, en particulier les considérations d’accessibilité intégrées et standardisées.J’attends le jour où l’on pourra l’utiliser sans polyfill, une fois que Safari avant 15.4 sortira des critères de prise en charge.
Mais mon principal reproche est sa dépendance à JavaScript. Presque tous les visiteurs d’un site auront JavaScript activé, mais il y a une certaine satisfaction à faire en sorte que ce ne soit pas nécessaire.
Je ne comprends pas pourquoi on ne peut pas contrôler l’état ouvert d’une boîte de dialogue avec CSS ou un bouton cible, et j’aimerais le savoir si je me trompe.
popover="".Ils peuvent facilement être ciblés par un bouton, ne nécessitent pas de JavaScript côté client, et peuvent aussi se fermer en cliquant à l’extérieur. C’est un comportement que
<dialog>n’offre pas par défaut.Je ne suis pas sûr pour l’accessibilité, mais cela me semble au moins pas pire que les anciennes approches avec labels/cases à cocher/éléments de formulaire cachés, tout en étant beaucoup plus simple et moins bricolé.
https://css-tricks.com/invoker-commands-additional-ways-to-w...
J’aimerais que HTML prenne en charge un concept de balise comme
<page>, permettant de définir plusieurs pages dans un seul fichier et d’en afficher une seule à la fois.Sans que cela ressemble à une boîte de dialogue, chaque PAGE pourrait, selon l’état sélectionné, réutiliser des sections communes de la même page comme l’en-tête, la barre latérale ou le pied de page.
On peut déjà le faire aujourd’hui en masquant et affichant des
div, mais un tag HTML spécifique pour prendre en charge cette approche pourrait être utile.En l’utilisant aujourd’hui, je suis tombé sur un problème que je n’ai pas réussi à contourner.
Quand il y a un formulaire dans une boîte de dialogue, appuyer sur Entrée avec le focus dans un champ, ou sur Espace avec le focus sur le bouton d’envoi, ferme la boîte de dialogue en même temps que le formulaire est soumis.
Je n’ai pas trouvé de moyen propre de l’empêcher. D’habitude, un formulaire recharge la page, donc ce n’est probablement pas un problème courant, mais ici j’utilisais htmx.
J’utilise un éditeur où un formulaire dans un
<dialog>met à jour un iframe, et seul l’iframe cible se recharge ; la boîte de dialogue ne se ferme pas.Je n’ai pas non plus réussi à reproduire le cas où la boîte de dialogue se ferme en appuyant sur Entrée. Cela devrait être équivalent à
submit, etsubmitne ferme pas non plus la boîte de dialogue.Au passage, j’ai appris hier que les boutons peuvent fermer une boîte de dialogue.
preventDefaultetstopPropagationne suffiraient-ils pas ?En cherchant un gestionnaire de consentement aux cookies à intégrer dans une build récemment optimisée, je n’aimais pas le fait que les options open source dépassaient les 100 Ko, alors j’en ai créé un moi-même : https://github.com/replete/biscuitman
Pour le rendre aussi petit que possible, je me suis appuyé sur
<dialog>, et avec seulement quelques règles CSS, il fonctionne nativement même sans style.J’ai finalement aussi écrit un outil de build qui compile jusqu’à IE11 et de très anciennes versions de navigateurs.
<dialog>fonctionne globalement bien et, sur les anciens navigateurs, quelques astuces CSS sont nécessaires, mais ce n’est pas difficile à gérer. C’est un bon ajout à la plateforme Web, mais après 20 ans à faire ce métier, je n’ai pas envie de recréer un contrôle de sélection multiple personnalisé tous les quelques ans. Les contrôles natifs, c’est bien.Le comportement de fermeture habituel de la plupart des exemples ne fonctionne pas dans Firefox Android
autofocusdu bouton de fermeture ne fonctionnait pasAu final, j’ai ajouté une ligne du genre
$('#thatModal *[autofocus]').focus()à chaque appel deshow()D’après MDN, cela devrait fonctionner comme prévu par défaut
Il semble que seuls les écouteurs ajoutés en JavaScript fonctionnent correctement