Présentation générale de la création de mini et méga-modals adaptables aux couleurs, responsifs et accessibles avec l'élément <dialog>
.
Dans ce post, je veux partager mes réflexions
sur la façon de créer des couleurs adaptatives,
Mini et méga-modales réactifs et accessibles avec l'élément <dialog>
.
Essayez la version de démonstration et visionnez la
.
Si vous préférez la vidéo, voici une version YouTube de cet article:
Présentation
La
<dialog>
convient parfaitement à une action ou à des informations contextuelles intégrées sur la page. Tenez compte du moment où
l'expérience utilisateur peut bénéficier d'une même action sur une même page plutôt que sur plusieurs pages
l'action: peut-être parce que le formulaire est de petite taille ou que la seule action requise de la part du
confirmer ou annuler.
L'élément <dialog>
est depuis peu stable dans tous les navigateurs:
Navigateurs pris en charge
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
J'ai constaté que l'élément manquait quelques éléments, donc dans cette IUG Défi J'ajoute l'expérience développeur les éléments attendus: des événements supplémentaires, un effet de fond lumineux, des animations personnalisées et méga-type.
Majoration
Les éléments de base d'un élément <dialog>
sont simples. L'élément sera
sont automatiquement masquées et des styles sont intégrés pour se superposer à votre contenu.
<dialog>
…
</dialog>
Nous pouvons améliorer cette référence.
Traditionnellement, un élément de boîte de dialogue partage beaucoup avec un modal et souvent les noms
sont interchangeables. Je me suis permis d'utiliser l'élément de dialogue
à la fois de petites fenêtres pop-up de boîte de dialogue (mini) et des boîtes de dialogue pleine page (méga). J'ai nommé
méga et mini, les deux dialogues étant légèrement adaptés aux différents cas d'utilisation.
J'ai ajouté un attribut modal-mode
pour vous permettre de spécifier le type:
<dialog id="MegaDialog" modal-mode="mega"></dialog>
<dialog id="MiniDialog" modal-mode="mini"></dialog>
Pas toujours, mais les éléments de dialogue
seront généralement utilisés pour rassembler
des informations
d'interaction. Les formulaires à l'intérieur des éléments de boîte de dialogue sont faits pour le déplacement.
ensemble.
Il est judicieux d'avoir un élément de formulaire
encapsulant le contenu de la boîte de dialogue afin que
JavaScript peut accéder aux données saisies par l'utilisateur. En outre, les boutons situés à l'intérieur
un formulaire utilisant method="dialog"
peut fermer une boîte de dialogue sans JavaScript et transmettre
données.
<dialog id="MegaDialog" modal-mode="mega">
<form method="dialog">
…
<button value="cancel">Cancel</button>
<button value="confirm">Confirm</button>
</form>
</dialog>
Méga boîte de dialogue
Une boîte de dialogue méga contient trois éléments dans le formulaire:
<header>
,
<article>
,
et
<footer>
Ceux-ci servent de conteneurs sémantiques et de cibles de style pour
de la boîte de dialogue. L'en-tête intitule la modale et propose une conclusion
. Cet article concerne les entrées et informations de formulaire. Le pied de page
contient une
<menu>
sur
boutons d'action.
<dialog id="MegaDialog" modal-mode="mega">
<form method="dialog">
<header>
<h3>Dialog title</h3>
<button onclick="this.closest('dialog').close('close')"></button>
</header>
<article>...</article>
<footer>
<menu>
<button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
<button type="submit" value="confirm">Confirm</button>
</menu>
</footer>
</form>
</dialog>
Le premier bouton
de menu a
autofocus
et un gestionnaire d'événements intégré onclick
. L'attribut autofocus
recevra
quand la boîte de dialogue est ouverte, et je trouve qu'il est recommandé
le bouton d'annulation, et non
le bouton de confirmation. Cela garantit que la confirmation est
délibéré et non accidentel.
Mini-boîte de dialogue
La mini-boîte de dialogue est très semblable à la méga boîte de dialogue, mais il lui manque simplement
Élément <header>
. Cela lui permet d'être plus petit et plus aligné.
<dialog id="MiniDialog" modal-mode="mini">
<form method="dialog">
<article>
<p>Are you sure you want to remove this user?</p>
</article>
<footer>
<menu>
<button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
<button type="submit" value="confirm">Confirm</button>
</menu>
</footer>
</form>
</dialog>
L'élément de boîte de dialogue constitue une base solide pour un élément de fenêtre d'affichage complet peut collecter des données et les interactions des utilisateurs. Ces principes essentiels peuvent rendre des interactions intéressantes et efficaces sur votre site ou dans votre application.
Accessibilité
L'élément de boîte de dialogue offre une très bonne accessibilité intégrée. Au lieu d'ajouter ces fonctionnalités comme d’habitude, beaucoup existent déjà.
Restauration de la mise au point...
Comme nous l'avons fait manuellement dans la section Créer un panneau de navigation latéral , il est important que Le fait d'ouvrir et de fermer quelque chose permet de mettre l'accent sur l'ouverture et la fermeture correspondantes. . Lorsque ce panneau de navigation s'ouvre, le curseur est placé sur le bouton de fermeture. Lorsque lorsque l'utilisateur appuie sur le bouton de fermeture, le curseur est rétabli sur le bouton qui l'a ouvert.
Pour l'élément de boîte de dialogue, il s'agit du comportement intégré par défaut:
Malheureusement, si vous voulez animer la boîte de dialogue à l'intérieur et à l'extérieur, cette fonctionnalité est perdu. Dans la section JavaScript, je vais le restaurer de Google Cloud.
Reprise de la mise au point
L'élément de boîte de dialogue gère
inert
sur le document. Avant le inert
, JavaScript était utilisé pour surveiller le focus
quittant un élément, auquel cas il l'intercepte et le replace.
Navigateurs pris en charge
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Après le inert
, n'importe quelle partie du document peut être "figée" à condition qu'elles soient
cibles ou qui sont interactives avec la souris. Au lieu de piéger
le focus est dirigé vers la seule partie interactive du document.
Ouvrir un élément et effectuer la mise au point automatique
Par défaut, l'élément de boîte de dialogue sélectionne le premier élément sélectionnable
dans le balisage de la boîte de dialogue. Si ce n'est pas le meilleur élément
que l'utilisateur utilise par défaut,
utilisez l'attribut autofocus
. Comme indiqué précédemment, je pense qu'il est recommandé
pour le mettre sur le bouton d'annulation
et non sur le bouton de confirmation. Cela garantit que
cette confirmation est délibérée et n'est pas accidentelle.
Fermer avec la touche Échap
Il est important de faciliter la fermeture de cet élément potentiellement intrusive. Heureusement, l'élément de boîte de dialogue gère automatiquement la touche Échap, ce qui vous permet de la charge de l'orchestration.
Styles
Il existe un moyen simple de styliser l'élément de boîte de dialogue et un chemin d'accès difficile. La facilité
s'effectue en ne modifiant pas la propriété d'affichage de la boîte de dialogue,
avec ses limites. Je m'efforce de fournir
des animations personnalisées pour
ouvrir et fermer la boîte de dialogue, en reprenant la propriété display
, etc.
Appliquer un style avec des accessoires ouverts
Pour accélérer les couleurs adaptatives et la cohérence globale de la conception, j'ai importé ma bibliothèque de variables CSS Open Props. Dans en plus des variables fournies sans frais, j'importe aussi normalize ainsi que d'autres buttons. Tous deux OpenProps fournit en tant qu'importations facultatives. Ces importations m'aident à me concentrer sur la personnalisation une boîte de dialogue et une démonstration sans avoir besoin de beaucoup de styles pour la prendre en charge et lui donner l'apparence bien.
Appliquer un style à l'élément <dialog>
Posséder la propriété d'affichage
Le comportement d'affichage et de masquage par défaut d'un élément de boîte de dialogue active ou désactive l'affichage
de block
à none
. Cela signifie malheureusement qu'il ne peut pas être animé
à l'intérieur et à l'extérieur, uniquement à l'intérieur. j'aimerais effectuer une animation
à l'intérieur et à l'extérieur. La première étape est
définir ma propre
display:
dialog {
display: grid;
}
En modifiant, et donc en détenant la valeur de la propriété d'affichage, comme indiqué dans les au-dessus de l'extrait CSS, vous devez gérer une grande quantité de styles pour pour assurer une bonne expérience utilisateur. Tout d'abord, l'état par défaut d'une boîte de dialogue est fermé. Vous pouvez représenter cet état visuellement et empêcher la boîte de dialogue reçoivent des interactions avec les styles suivants:
dialog:not([open]) {
pointer-events: none;
opacity: 0;
}
La boîte de dialogue est désormais invisible et il n'est pas possible d'interagir avec elle lorsqu'elle n'est pas ouverte. Plus tard
Je vais ajouter du code JavaScript pour gérer l'attribut inert
dans la boîte de dialogue, en veillant à ce que
que les utilisateurs de clavier et de lecteurs d'écran
ne peuvent pas non plus accéder à la boîte de dialogue masquée.
Attribuer à la boîte de dialogue un thème de couleurs adaptatifs
Lorsque color-scheme
active votre document dans un navigateur fourni
en s'adaptant aux préférences système claires et sombres,
l’élément de boîte de dialogue plus que cela. Open Props propose des surfaces
de couleurs qui s'adaptent automatiquement
des préférences système claires et sombres, de la même manière que lorsque vous utilisez color-scheme
. Ces
sont parfaits pour créer des calques dans une conception
et j’aime utiliser la couleur pour m’aider
qui illustrent visuellement cette apparence de surfaces de calques. La couleur d'arrière-plan est
var(--surface-1)
; Pour vous placer au-dessus de cette couche, utilisez var(--surface-2)
:
dialog {
…
background: var(--surface-2);
color: var(--text-1);
}
@media (prefers-color-scheme: dark) {
dialog {
border-block-start: var(--border-size-1) solid var(--surface-3);
}
}
D'autres couleurs adaptatives seront ajoutées ultérieurement pour les éléments enfants, comme l'en-tête et pied de page. Je les considère comme supplémentaires pour un élément de dialogue, mais c’est très important dans pour créer une conception de dialogue convaincante et bien conçue.
Taille de la boîte de dialogue responsive
Par défaut, la boîte de dialogue délègue sa taille à son contenu, ce qui est généralement
très bien. Mon objectif est de limiter
max-inline-size
à une taille lisible (--size-content-3
= 60ch
) ou à 90% de la largeur de la fenêtre d'affichage. Ce
garantit que la boîte de dialogue ne s'affiche pas d'un bord à l'autre sur un appareil mobile,
sur un écran d'ordinateur
de bureau difficile à lire. Ensuite, j'ajoute une
max-block-size
afin que la boîte de dialogue ne dépasse pas la hauteur de la page. Cela signifie
également que nous
spécifier l'emplacement de la zone déroulante de la boîte de dialogue, pour le cas où il s'agirait d'une grande
"Dialog".
dialog {
…
max-inline-size: min(90vw, var(--size-content-3));
max-block-size: min(80vh, 100%);
max-block-size: min(80dvb, 100%);
overflow: hidden;
}
Vous voyez que j'ai max-block-size
deux fois ? La première utilise 80vh
, un objet physique
bloc de fenêtre d'affichage. Ce que je veux vraiment, c'est que
la boîte de dialogue reste dans un flux relatif,
pour les utilisateurs internationaux, j'utilise donc la logique, la plus récente,
prise en charge dans l'unité dvb
dans la deuxième déclaration.
Positionnement de la méga-boîte de dialogue
Pour vous aider à positionner un élément de boîte de dialogue, il est utile de décomposer ses deux le fond en plein écran et le conteneur de la boîte de dialogue. Le fond doit couvrira tout, offrant un effet d'ombre pour confirmer que cette boîte de dialogue est au premier plan et le contenu derrière est inaccessible. Le conteneur de boîte de dialogue est libre de se centrer sur ce fond et prendre la forme dont son contenu a besoin.
Les styles suivants fixent l'élément de boîte de dialogue à la fenêtre, en l'étirant à chaque
et utilise margin: auto
pour centrer le contenu:
dialog {
…
margin: auto;
padding: 0;
position: fixed;
inset: 0;
z-index: var(--layer-important);
}
Styles de boîte de dialogue "Méga" mobiles
Pour les petites fenêtres d'affichage, le style de cette mégamodale pleine page est légèrement différent. Je
La marge inférieure est définie sur 0
. Le contenu de la boîte de dialogue s'affiche alors au bas de la
la fenêtre d'affichage. Après quelques ajustements de style, je peux transformer la boîte de dialogue en
fiche d'action, plus près des pouces de l'utilisateur:
@media (max-width: 768px) {
dialog[modal-mode="mega"] {
margin-block-end: 0;
border-end-end-radius: 0;
border-end-start-radius: 0;
}
}
Positionnement de la mini-boîte de dialogue
Lorsque j'utilise une fenêtre d'affichage plus grande, comme sur un ordinateur de bureau, j'ai choisi de placer les mini boîtes de dialogue par-dessus l'élément qui les a appelés. Pour cela, j'ai besoin de JavaScript. Vous trouverez technique que j'utilise cliquez ici, mais je pense que cela dépasse le cadre de cet article. Sans JavaScript, le une mini boîte de dialogue apparaît au centre de l'écran, tout comme la boîte de dialogue "méga".
Faites ressortir votre corps
Enfin, ajoutez du style à la boîte de dialogue pour qu'elle ressemble à une surface douce au-dessus de la page. La douceur est obtenue en arrondissant les coins des dialogues. La profondeur est obtenue avec l'une des ombres conçues avec soin d'Open Props. props:
dialog {
…
border-radius: var(--radius-3);
box-shadow: var(--shadow-6);
}
Personnaliser le pseudo-élément Backdrop
J'ai choisi de travailler très légèrement avec le fond, en ajoutant simplement un effet de flou
backdrop-filter
à la boîte de dialogue Méga:
Navigateurs pris en charge
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
dialog[modal-mode="mega"]::backdrop {
backdrop-filter: blur(25px);
}
J'ai également choisi d'effectuer une transition vers backdrop-filter
, en espérant que les navigateurs
permettra la transition de l'élément Backdrop à l'avenir:
dialog::backdrop {
transition: backdrop-filter .5s ease;
}
Bonus de style
J'appelle cette section « suppléments » car c'est davantage lié à l'élément de dialogue que l'élément de boîte de dialogue en général.
Confinement du défilement
Lorsque la boîte de dialogue est affichée, l'utilisateur peut toujours faire défiler la page située derrière, ce que je ne veux pas:
Habituellement,
overscroll-behavior
est la solution que j'utilise habituellement, mais conformément aux
caractéristiques,
cela n'a aucun effet sur la boîte de dialogue, car il ne s'agit pas d'un port de défilement, c'est-à-dire
un conteneur de défilement.
Il n'y a donc rien à empêcher. je pourrais utiliser JavaScript
pour surveiller
les nouveaux événements de ce guide, tels que "fermé" et « ouvert », et activer
overflow: hidden
sur le document, ou je pourrais attendre que :has()
soit stable dans
tous les navigateurs:
Navigateurs pris en charge
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
html:has(dialog[open][modal-mode="mega"]) {
overflow: hidden;
}
Désormais, lorsqu'une grande boîte de dialogue est ouverte, le document HTML contient overflow: hidden
.
La mise en page <form>
En plus d'être un élément très important pour
recueillir l'interaction
les informations de l'utilisateur, je les utilise
ici pour mettre en page l'en-tête, le pied de page et
de l'article. Avec cette mise en page, j'envisage
d'articuler l'enfant de l'article comme
que vous pouvez faire défiler. J'y arrive avec
grid-template-rows
L'élément d'article reçoit la valeur 1fr
et le formulaire lui-même a la même valeur maximale
comme élément de boîte de dialogue. Qu'est-ce qui permet de définir une hauteur et une taille de ligne fermes ?
permet de contraindre l'élément de l'article et de le faire défiler en cas de dépassement:
dialog > form {
display: grid;
grid-template-rows: auto 1fr auto;
align-items: start;
max-block-size: 80vh;
max-block-size: 80dvb;
}
Appliquer un style à la boîte de dialogue <header>
Le rôle de cet élément est de fournir un titre pour le contenu de la boîte de dialogue et l'offre un bouton de fermeture facile à trouver. Une couleur de surface lui est également attribuée pour faire apparaître se trouver derrière le contenu de l'article de la boîte de dialogue. Ces exigences conduisent à un Flexbox des éléments alignés verticalement et espacés par rapport à leurs bords, et certains des marges intérieures et des blancs pour laisser de la place au titre et aux boutons de fermeture:
dialog > form > header {
display: flex;
gap: var(--size-3);
justify-content: space-between;
align-items: flex-start;
background: var(--surface-2);
padding-block: var(--size-3);
padding-inline: var(--size-5);
}
@media (prefers-color-scheme: dark) {
dialog > form > header {
background: var(--surface-1);
}
}
Appliquer un style au bouton de fermeture de l'en-tête
Étant donné que la démo utilise les boutons "Open Props" (Ouvrir les accessoires), le bouton de fermeture est personnalisé en un bouton centré sur une icône ronde comme ceci:
dialog > form > header > button {
border-radius: var(--radius-round);
padding: .75ch;
aspect-ratio: 1;
flex-shrink: 0;
place-items: center;
stroke: currentColor;
stroke-width: 3px;
}
Appliquer un style à la boîte de dialogue <article>
L'élément article a un rôle particulier dans cette boîte de dialogue: il s'agit d'un espace destiné à défile dans le cas d'une boîte de dialogue allongée ou longue.
Pour ce faire, l'élément de formulaire parent a défini des limites
qui définissent les contraintes à atteindre
pour cet élément de l'article s'il reçoit
trop grande. Définissez overflow-y: auto
pour que les barres de défilement ne s'affichent que si nécessaire.
contiennent des caractères de défilement avec overscroll-behavior: contain
, et le reste
seront personnalisés:
dialog > form > article {
overflow-y: auto;
max-block-size: 100%; /* safari */
overscroll-behavior-y: contain;
display: grid;
justify-items: flex-start;
gap: var(--size-3);
box-shadow: var(--shadow-2);
z-index: var(--layer-1);
padding-inline: var(--size-5);
padding-block: var(--size-3);
}
@media (prefers-color-scheme: light) {
dialog > form > article {
background: var(--surface-1);
}
}
Appliquer un style à la boîte de dialogue <footer>
Le rôle du pied de page est de contenir des menus de boutons d'action. Flexbox est utilisé pour d'aligner le contenu à la fin de l'axe aligné du pied de page, puis un espacement laisser de la place aux boutons.
dialog > form > footer {
background: var(--surface-2);
display: flex;
flex-wrap: wrap;
gap: var(--size-3);
justify-content: space-between;
align-items: flex-start;
padding-inline: var(--size-5);
padding-block: var(--size-3);
}
@media (prefers-color-scheme: dark) {
dialog > form > footer {
background: var(--surface-1);
}
}
Styliser le menu de pied de page de la boîte de dialogue
menu
est utilisé pour contenir les boutons d'action de la boîte de dialogue. Elle utilise un code d'encapsulation
mise en page Flexbox avec gap
pour laisser de l'espace entre les boutons. Éléments de menu
une marge intérieure, comme <ul>
. Je supprime également ce style, car je n'en ai pas besoin.
dialog > form > footer > menu {
display: flex;
flex-wrap: wrap;
gap: var(--size-3);
padding-inline-start: 0;
}
dialog > form > footer > menu:only-child {
margin-inline-start: auto;
}
Animation
Les éléments de boîte de dialogue sont souvent animés, car ils entrent et sortent de la fenêtre. Intégrer dans les boîtes de dialogue un mouvement de soutien à l'entrée et à la sortie aide les utilisateurs s’orienter dans le flux.
Normalement, l'élément de boîte de dialogue ne peut être animé que vers l'intérieur, et non vers l'extérieur. En effet,
le navigateur active la propriété display
sur l'élément. Précédemment, le guide
l'affichage en mode Grille
et ne le configure jamais sur aucune. Cela vous permet de
s'animent à l'intérieur et à l'extérieur.
Open Props est fourni avec de nombreuses images clés des animations, ce qui facilite l'orchestration doit être facile et lisible. Voici les objectifs de l'animation approche que j'ai adoptée:
- Le mouvement réduit est la transition par défaut, un fondu à l'ouverture et à la fermeture d'opacité simple.
- Si le mouvement est autorisé, des animations de glissement et de mise à l'échelle sont ajoutées.
- La mise en page responsive pour mobile de la méga boîte de dialogue est ajustée pour glisser vers l'extérieur.
Une transition par défaut sécurisée et efficace
Bien qu'Open Props ait des images clés
pour créer un fondu à l'ouverture et à la fermeture,
approche multicouche des transitions par défaut avec des animations d'images clés
des mises à niveau potentielles. Précédemment, nous avons stylisé la visibilité de la boîte de dialogue avec
opacité, orchestration de 1
ou 0
en fonction de l'attribut [open]
. À
entre 0% et 100%, indiquer au navigateur la durée et le type
lissage de vitesse souhaité:
dialog {
transition: opacity .5s var(--ease-3);
}
Ajouter du mouvement à la transition
Si l'utilisateur est d'accord avec les mouvements, les boîtes de dialogue "méga" et "mini" doivent glisser.
à l'entrée et à la réduction à mesure qu'elles sortent. Pour ce faire, vous pouvez utiliser
prefers-reduced-motion
requête média et quelques accessoires ouverts:
@media (prefers-reduced-motion: no-preference) {
dialog {
animation: var(--animation-scale-down) forwards;
animation-timing-function: var(--ease-squish-3);
}
dialog[open] {
animation: var(--animation-slide-in-up) forwards;
}
}
Adapter l'animation de sortie aux appareils mobiles
Plus tôt dans la section consacrée aux styles, le style "méga-boîte de dialogue" est adapté aux mobiles ces appareils s'apparentent davantage à une feuille d'action, comme si une petite feuille de papier avait glissé vers le haut à partir du bas de l'écran et reste fixé en bas. La balance l'animation de sortie n'est pas adaptée à ce nouveau design. quelques requêtes média et quelques Propositions ouvertes:
@media (prefers-reduced-motion: no-preference) and @media (max-width: 768px) {
dialog[modal-mode="mega"] {
animation: var(--animation-slide-out-down) forwards;
animation-timing-function: var(--ease-squish-2);
}
}
JavaScript
Vous devez ajouter plusieurs éléments avec JavaScript:
// dialog.js
export default async function (dialog) {
// add light dismiss
// add closing and closed events
// add opening and opened events
// add removed event
// removing loading attribute
}
Ces ajouts sont nés de la volonté de fermer le champ de vision (en cliquant sur la boîte de dialogue un fond d'écran), des animations et d'autres événements pour accélérer les données du formulaire.
Ajout de la fonctionnalité Ignorer
Cette tâche est simple et complète un excellent élément de dialogue qui n'est pas
en cours d'animation. L'interaction est obtenue en regardant les clics sur la boîte de dialogue
et en tirant parti des événements
bouillonnement
pour évaluer l'élément sur lequel l'utilisateur a cliqué,
close()
s'il s'agit de l'élément de niveau supérieur:
export default async function (dialog) {
dialog.addEventListener('click', lightDismiss)
}
const lightDismiss = ({target:dialog}) => {
if (dialog.nodeName === 'DIALOG')
dialog.close('dismiss')
}
Notez dialog.close('dismiss')
. L'événement est appelé et une chaîne est fournie.
Cette chaîne peut être récupérée par un autre code JavaScript pour obtenir des informations sur la façon dont
la boîte de dialogue a été fermée. Vous constaterez que j'ai également fourni
des chaînes fermées chaque fois que j'appelle
la fonction à partir de différents boutons, afin de fournir à mon application des informations sur
l’interaction de l’utilisateur.
Ajouter des événements de fermeture et de fermeture
L'élément de boîte de dialogue est fourni avec un événement de fermeture: il est émis immédiatement lorsque l'événement
La fonction close()
de boîte de dialogue est appelée. Puisque nous animons cet élément,
Il est utile d'avoir des événements avant et après l'animation, pour récupérer
données ou réinitialiser le formulaire de la boîte de dialogue. Je l'utilise ici pour gérer l'ajout
inert
sur la boîte de dialogue fermée, que j'utilise dans la démonstration pour le modifier
la liste des avatars si l'utilisateur a envoyé une nouvelle image.
Pour ce faire, créez deux événements nommés closing
et closed
. Ensuite,
écouter l'événement de fermeture
intégré sur la boîte de dialogue. À partir de là, définissez la boîte de dialogue sur
inert
et envoyez l'événement closing
. La prochaine étape consiste à attendre que
des animations et des transitions pour terminer l'exécution sur la boîte de dialogue, puis déclencher
Événement closed
.
const dialogClosingEvent = new Event('closing')
const dialogClosedEvent = new Event('closed')
export default async function (dialog) {
…
dialog.addEventListener('close', dialogClose)
}
const dialogClose = async ({target:dialog}) => {
dialog.setAttribute('inert', '')
dialog.dispatchEvent(dialogClosingEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogClosedEvent)
}
const animationsComplete = element =>
Promise.allSettled(
element.getAnimations().map(animation =>
animation.finished))
La fonction animationsComplete
, également utilisée lors de la création d'un toast
, renvoie une promesse basée sur le
la réalisation de l'animation
et les promesses de transition. C'est pourquoi dialogClose
est une commande asynchrone
function;
il peut ensuite
await
la promesse est renvoyée et d’avancer
en toute confiance vers l’événement de clôture.
Ajouter des événements d'ouverture et d'ouverture
Ces événements ne sont pas aussi faciles à ajouter, car l'élément de boîte de dialogue intégré pour fournir un événement ouvert comme il le fait avec « close ». J'utilise un MutationObserver pour mieux comprendre l'évolution des attributs de la boîte de dialogue. Dans cet observateur, Je surveillerai les modifications apportées à l'attribut "open" et je gérerai les événements personnalisés en conséquence.
De la même manière que nous avons commencé les événements de bouclage et de clôture, créez deux nouveaux événements
appelé opening
et opened
. À l'endroit où nous avons précédemment écouté la fermeture de la boîte de dialogue
cette fois, utilisez un observateur de mutation créé pour surveiller l'événement
.
…
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent = new Event('opened')
export default async function (dialog) {
…
dialogAttrObserver.observe(dialog, {
attributes: true,
})
}
const dialogAttrObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(async mutation => {
if (mutation.attributeName === 'open') {
const dialog = mutation.target
const isOpen = dialog.hasAttribute('open')
if (!isOpen) return
dialog.removeAttribute('inert')
// set focus
const focusTarget = dialog.querySelector('[autofocus]')
focusTarget
? focusTarget.focus()
: dialog.querySelector('button').focus()
dialog.dispatchEvent(dialogOpeningEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogOpenedEvent)
}
})
})
La fonction de rappel de l'observateur de mutation est appelée lorsque la boîte de dialogue
sont modifiés et fournissent la liste des modifications sous forme de tableau. Itérer sur
l'attribut change et l'élément attributeName
doit être ouvert. Ensuite, vérifiez
Si l'élément possède ou non l'attribut: indique si la boîte de dialogue
est désormais ouverte. S'il a été ouvert, supprimez l'attribut inert
et placez le curseur.
à un élément demandant
autofocus
ou le premier élément button
trouvé dans la boîte de dialogue. Enfin, comme la formule de bouclage
et l'événement fermé, envoyer l'événement d'ouverture immédiatement, attendre que les animations
pour terminer, puis envoyer l'événement ouvert.
Ajouter un événement supprimé
Dans les applications monopages, les boîtes de dialogue sont souvent ajoutées et supprimées en fonction des routes ou d'autres besoins et états de l'application. Il peut être utile de nettoyer les événements ou des données lorsqu'une boîte de dialogue est supprimée.
Pour ce faire, vous pouvez utiliser un autre observateur de mutation. Cette fois, au lieu de observant des attributs sur un élément de boîte de dialogue, nous allons observer les enfants du corps et surveiller la suppression des éléments de boîte de dialogue.
…
const dialogRemovedEvent = new Event('removed')
export default async function (dialog) {
…
dialogDeleteObserver.observe(document.body, {
attributes: false,
subtree: false,
childList: true,
})
}
const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(mutation => {
mutation.removedNodes.forEach(removedNode => {
if (removedNode.nodeName === 'DIALOG') {
removedNode.removeEventListener('click', lightDismiss)
removedNode.removeEventListener('close', dialogClose)
removedNode.dispatchEvent(dialogRemovedEvent)
}
})
})
})
Le rappel de l'observateur de mutation est appelé chaque fois que des enfants sont ajoutés ou supprimés.
à partir du corps du document. Les mutations spécifiques surveillées
Les removedNodes
qui ont le
nodeName
sur
une boîte de dialogue. Si une boîte de dialogue a été supprimée, les événements de clic et de fermeture sont supprimés
pour libérer de la mémoire, et l'événement personnalisé supprimé est envoyé.
Supprimer l'attribut de chargement
Pour empêcher l'animation de lecture de la boîte de dialogue de sortie lorsqu'elle est ajoutée à la page ou lors du chargement de la page, un attribut de chargement a été ajouté à la boîte de dialogue. La le script suivant attend la fin de l'exécution des animations de boîte de dialogue, puis supprime l'attribut. L'animation de la boîte de dialogue de cacher une animation autrement distrayante.
export default async function (dialog) {
…
await animationsComplete(dialog)
dialog.removeAttribute('loading')
}
Découvrez comment empêcher l'animation d'images clés lors du chargement de la page. en cliquant ici.
L'union fait la force
Voici dialog.js
dans son intégralité, maintenant que nous avons expliqué chaque section
individuellement:
// custom events to be added to <dialog>
const dialogClosingEvent = new Event('closing')
const dialogClosedEvent = new Event('closed')
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent = new Event('opened')
const dialogRemovedEvent = new Event('removed')
// track opening
const dialogAttrObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(async mutation => {
if (mutation.attributeName === 'open') {
const dialog = mutation.target
const isOpen = dialog.hasAttribute('open')
if (!isOpen) return
dialog.removeAttribute('inert')
// set focus
const focusTarget = dialog.querySelector('[autofocus]')
focusTarget
? focusTarget.focus()
: dialog.querySelector('button').focus()
dialog.dispatchEvent(dialogOpeningEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogOpenedEvent)
}
})
})
// track deletion
const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(mutation => {
mutation.removedNodes.forEach(removedNode => {
if (removedNode.nodeName === 'DIALOG') {
removedNode.removeEventListener('click', lightDismiss)
removedNode.removeEventListener('close', dialogClose)
removedNode.dispatchEvent(dialogRemovedEvent)
}
})
})
})
// wait for all dialog animations to complete their promises
const animationsComplete = element =>
Promise.allSettled(
element.getAnimations().map(animation =>
animation.finished))
// click outside the dialog handler
const lightDismiss = ({target:dialog}) => {
if (dialog.nodeName === 'DIALOG')
dialog.close('dismiss')
}
const dialogClose = async ({target:dialog}) => {
dialog.setAttribute('inert', '')
dialog.dispatchEvent(dialogClosingEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogClosedEvent)
}
// page load dialogs setup
export default async function (dialog) {
dialog.addEventListener('click', lightDismiss)
dialog.addEventListener('close', dialogClose)
dialogAttrObserver.observe(dialog, {
attributes: true,
})
dialogDeleteObserver.observe(document.body, {
attributes: false,
subtree: false,
childList: true,
})
// remove loading attribute
// prevent page load @keyframes playing
await animationsComplete(dialog)
dialog.removeAttribute('loading')
}
Utiliser le module dialog.js
La fonction exportée du module s'attend à être appelée et transmise à une boîte de dialogue qui souhaite ajouter ces événements et cette nouvelle fonctionnalité:
import GuiDialog from './dialog.js'
const MegaDialog = document.querySelector('#MegaDialog')
const MiniDialog = document.querySelector('#MiniDialog')
GuiDialog(MegaDialog)
GuiDialog(MiniDialog)
Les deux boîtes de dialogue ont été mises à jour avec le chargement de correctifs et d'autres événements à traiter.
Écouter les nouveaux événements personnalisés
Chaque élément de boîte de dialogue mis à niveau peut désormais écouter cinq nouveaux événements, comme ceci:
MegaDialog.addEventListener('closing', dialogClosing)
MegaDialog.addEventListener('closed', dialogClosed)
MegaDialog.addEventListener('opening', dialogOpening)
MegaDialog.addEventListener('opened', dialogOpened)
MegaDialog.addEventListener('removed', dialogRemoved)
Voici deux exemples de gestion de ces événements:
const dialogOpening = ({target:dialog}) => {
console.log('Dialog opening', dialog)
}
const dialogClosed = ({target:dialog}) => {
console.log('Dialog closed', dialog)
console.info('Dialog user action:', dialog.returnValue)
if (dialog.returnValue === 'confirm') {
// do stuff with the form values
const dialogFormData = new FormData(dialog.querySelector('form'))
console.info('Dialog form data', Object.fromEntries(dialogFormData.entries()))
// then reset the form
dialog.querySelector('form')?.reset()
}
}
Dans la démonstration que j'ai créée avec l'élément "Dialog", j'utilise l'événement fermé les données du formulaire pour ajouter un nouvel élément avatar à la liste. Le timing est bon dans que l'animation de sortie de la boîte de dialogue est terminée, et que certains scripts sont animés. dans le nouvel avatar. Les nouveaux événements permettent d'orchestrer l'expérience utilisateur. peut être plus fluide.
Remarquez dialog.returnValue
: cet élément contient la chaîne de fermeture transmise lorsque l'événement
l'événement close()
de la boîte de dialogue est appelé. Dans l'événement dialogClosed
, il est essentiel de
savoir si la boîte de dialogue
a été fermée, annulée ou confirmée. Si c'est confirmé, le
récupère les valeurs du formulaire et le réinitialise. La réinitialisation est utile pour
Lorsque la boîte de dialogue s'affiche à nouveau, elle est vide et prête pour un nouvel envoi.
Conclusion
Maintenant que vous savez comment j'ai fait, comment feriez-vous ? 😃
Diversifiez nos approches et découvrons toutes les manières de créer des applications sur le Web.
Créer une démonstration, me envoyer des tweets et je l'ajouterai à la section des remix de la communauté ci-dessous.
Remix de la communauté
- @GrimLink avec une caméra 3-en-1 .
- @mikemai2awesome avec un beau
remix ne modifie pas
display
. - @geoffrich_ avec Svelte et agréable Peau polie FLIP.
Ressources
- Code source sur GitHub
- Avatars doodle