Retour aux articles

Utiliser l’élément HTML dialog pour construire ses modales

De nombreux articles mettent en avant la simplicité d’utilisation de l’élément HTML dialog pour construire des modales. Il y a cependant certaines spécificités à surmonter si l’on souhaite se baser dessus pour mettre en place un système de modales complet dans nos projet.

Une modale simple

Pour une modale avec en-tête, corp et pied qui utilise l’élément dialog voici le HTML :

<dialog id="theDialog">
    <div class="dialog-header">
        <h2>The modal title</h2>
        <span data-closeModal="true">X</span>
    </div>
    <div class="dialog-body">
        <p>
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere, consectetur officiis possimus earum
            inventore nostrum quia modi doloribus! Impedit perferendis ab velit nisi a saepe veniam sit,
            accusamus beatae. Iusto quisquam autem nostrum reprehenderit, vero incidunt iure porro, nulla id
            harum, corporis eos eligendi at delectus tempore quo doloremque aliquid.
        </p>
    </div>
    <div class="dialog-footer">
        <button class="gray" data-closeModal="true" >Fermer</button>
        <button >Valider</button>
    </div>
</dialog>
<button class="btn" onclick="theModal.showModal()">
    Open the modal
</button>

Voici le CSS :

dialog{
  border: none;
  border-radius: 1rem;
  width: min( 100% - 3rem, 800px );
	flex-direction: column;
  justify-content: space-between;
  padding: 0;
}
dialog[open]{
	display: flex;
}
dialog>div{
  padding: 1rem;
}
dialog>:is(.dialog-header, .dialog-footer){
  flex: 60px;
}
dialog>.dialog-header{
  border-bottom: 1px solid #eee;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
dialog>.dialog-header span{
  cursor: pointer;
  font-weight: 700;
  width: 50px;
  text-align: center;
  opacity: 0.5;
}
dialog>.dialog-footer{
  border-top: 1px solid #eee;
  text-align: right;
}

Et voici le Javascript :

const theModal = document.getElementById('theDialog');

theModal.querySelectorAll( 'button[data-closeModal]' ).forEach( oneButton => {
    oneButton.onclick = function () {
        theModal.close();
    };
});

L’élément dialog est disponible nativement en HTML5 avec :

  • Des méthodes pour ouvrir/fermer la modale
  • Un pseudo-élément backdrop qui correspond au background
  • Une hauteur max qui l’empêche de ‘déborder’ hors du viewport
  • Une gestion automatique du z-index. Si vous ouvrez une modale, elle est directement au premier plan.

Tout cela simplifie la structure HTML à mettre en place, mais également le Javascript et le CSS. Il reste cependant quelques spécificités.

Flexbox et Scroll du corp de la modale

Dans notre implémentation, l’élément dialog est en display:flex et flex-direction:column, ce qui permet de conserver le header et le footer en haut et en bas de l’élément dialog. Le body pourra également prendre le reste de la hauteur disponible.

Si le contenu de l’élément dialog est trop important et qu’il atteint sa hauteur max ( la hauteur du viewport ), il va devenir scrollable dans son ensemble. Ce n’est pas ce que l’on souhaite dans la majorité des cas. On veut que seul le body soit scrollable si besoin :

Pour cela, on rajoute un peu de CSS :

dialog>.dialog-body{
    overflow: auto;
}

Mettre en forme le fond

Le ‘fond’ de la modale est un pseudo élément backdrop que l’on pourra styliser soi même :

dialog::backdrop {
    background: rgba(80, 80, 80, 0.2);
    backdrop-filter: blur(10px);
}

Fermeture de la modale en cliquant sur le fond

Il n’y a pas de mécanisme natif pour fermer la modale lorsque l’on clique sur le fond, il faut le mettre en place soi même :

theModal.addEventListener("click", function( event ){
    if ( event.target === theModal ) 
    {
        theModal.close();
    }
});

Animation d’ouverture et de fermeture

L’animation d’ouverture est simple à mettre en place en CSS :

dialog[open] {
        animation: showDialog 0.3s ease normal;
    }
    @keyframes showDialog{
        from {opacity: 0;transform: translateY(-20%);}
        to {opacity: 1;transform: translateY(0%);}
    }

Par contre, la méthode native close() passe directement l’élément dialog en display:none.

Pour animer la fermeture du dialog, il faudra donc d’abord lui appliquer une animation puis, une fois l’animation terminée, appeler la méthode close(). On utilise pour cela un attribut closing pour déclencher l’animation en CSS :

dialog[closing] {
  animation: closeDialog 0.35s ease normal;
}
@keyframes closeDialog{
  from {opacity: 1;transform: translateY(0%);}
  to {opacity: 0;transform: translateY(-20%);}
}

let closeModal = ( theModal) => {
    theModal.setAttribute( 'closing', true );
    setTimeout(() => {
        theModal.close();
        theModal.removeAttribute( 'closing' );
    }, 300 );
}

theModal.querySelectorAll( 'button[data-closeModal]' ).forEach( oneButton => {
    oneButton.onclick = function () {
        closeModal( theModal );
    };
});

Et voila, on parvient à un reconstruire un système de modale complet avec l’élément dialog.