En août, profitez de 2 mois offerts sur Djaboo avec le code : DJABOO26 → J'en profite
Popup JavaScript : fenêtre modale personnalisée créée en HTML, CSS et JavaScript

Popup JavaScript : créer une fenêtre modale de A à Z en 2026

5/5 - (562 votes)

Ouvrir une boîte de dialogue, confirmer une suppression, afficher un formulaire sans quitter la page… les popups JavaScript font partie du quotidien de tout développeur web. Pourtant, entre les méthodes natives du navigateur, les modales construites à la main et la balise <dialog> HTML5, les approches ne manquent pas, et toutes ne se valent pas.

Ce guide couvre l’intégralité du sujet : des trois popups natifs hérités des années 2000 jusqu’à la méthode moderne recommandée en 2026, en passant par le code complet d’une fenêtre modale personnalisée, les règles d’accessibilité, les déclencheurs intelligents et les contraintes SEO à respecter sur mobile. Que vous partiez de zéro ou que vous souhaitiez moderniser un code existant, vous trouverez ici tout ce dont vous avez besoin.

Les 3 popups natifs du navigateur : alert, confirm et prompt

Avant d’écrire la moindre ligne de CSS, JavaScript propose trois méthodes intégrées pour afficher des fenêtres contextuelles. Elles sont simples, universellement supportées et ne nécessitent aucune dépendance.

`window.alert()` affiche un message et un bouton « OK ». L’exécution du script est bloquée jusqu’à ce que l’utilisateur clique.

// Affiche un message simple

window.alert("Votre fichier a bien été enregistré.");

`window.confirm()` affiche un message avec deux boutons (« OK » et « Annuler ») et retourne un booléen.

// Demande une confirmation avant suppression

const confirmation = window.confirm("Supprimer cet élément définitivement ?");

if (confirmation) {

supprimerElement();

}

`window.prompt()` affiche un champ de saisie et retourne la valeur entrée par l’utilisateur, ou null si l’utilisateur annule.

// Demande une valeur à l'utilisateur

const prenom = window.prompt("Quel est votre prénom ?", "Jean");

if (prenom !== null) {

console.log("Bonjour, " + prenom);

}

Pourquoi ces méthodes ne s’utilisent plus en production

Ces trois popups natifs souffrent de limitations rédhibitoires pour tout projet professionnel :

Blocage du thread principal. Pendant qu’une alert est ouverte, tout le JavaScript de la page est suspendu. Cela peut poser des problèmes avec les timers, les WebSockets ou les animations.
Apparence non personnalisable. Le style est entièrement dicté par le navigateur et le système d’exploitation. Impossible d’intégrer votre charte graphique.
Comportement bloqué dans les iframes. La plupart des navigateurs modernes désactivent ces méthodes dans les iframes cross-origin.
Expérience utilisateur dégradée. Ces boîtes de dialogue sont perçues comme intrusives et archaïques. Certains navigateurs les bloquent carrément si elles sont appelées trop fréquemment.
Aucune accessibilité avancée. Impossible d’y ajouter des attributs ARIA, de gérer le focus ou d’insérer du HTML structuré.

Ces méthodes restent utiles pour le débogage rapide ou les scripts d’automatisation simples. Pour tout ce qui touche à l’interface utilisateur en production, il faut construire une vraie fenêtre modale.

Créer une fenêtre modale personnalisée avec HTML, CSS et JavaScript

Une fenêtre modale personnalisée (aussi appelée popup HTML ou modal JavaScript) se compose de deux éléments : un overlay (le fond semi-transparent qui recouvre la page) et la boîte de dialogue elle-même. Voici le code complet, commenté ligne par ligne.

Structure HTML

<!-- Bouton qui déclenche l'ouverture de la modale -->

<button id="btn-ouvrir" type="button">Ouvrir la modale</button>



<!-- Overlay : fond semi-transparent qui recouvre la page -->

<div id="overlay" aria-hidden="true">



<!-- Boîte de dialogue : le contenu visible de la modale -->

<div

id="ma-modale"

class="modale"

role="dialog"

aria-modal="true"

aria-labelledby="modale-titre"

aria-describedby="modale-description"

>

<!-- En-tête avec titre et bouton de fermeture -->

<header>

<h2 id="modale-titre">Confirmer l'action</h2>

<button

id="btn-fermer"

class="btn-fermer"

type="button"

aria-label="Fermer la fenêtre modale"

>

<!-- Icône SVG de croix, masquée aux lecteurs d'écran -->

<svg aria-hidden="true" width="16" height="16" viewBox="0 0 16 16">

<path d="M12 4L4 12M4 4l8 8" stroke="currentColor" stroke-width="2"/>

</svg>

</button>

</header>



<!-- Corps de la modale -->

<div>

<p id="modale-description">

Êtes-vous sûr de vouloir effectuer cette action ? Elle est irréversible.

</p>

</div>



<!-- Pied de page avec les boutons d'action -->

<footer>

<button id="btn-annuler" type="button">Annuler</button>

<button id="btn-confirmer" type="button">Confirmer</button>

</footer>

</div>

</div>

Styles CSS

/* Overlay : fond semi-transparent, centrage de la modale */

.overlay {

position: fixed;

inset: 0; /* équivalent à top/right/bottom/left: 0 */

background-color: rgba(0, 0, 0, 0.55);

display: flex;

align-items: center;

justify-content: center;

z-index: 1000;

/* Masquée par défaut */

visibility: hidden;

opacity: 0;

transition: opacity 0.2s ease, visibility 0.2s ease;

}



/* État ouvert de l'overlay */

.overlay.est-ouverte {

visibility: visible;

opacity: 1;

}



/* Boîte de dialogue */

.modale {

background: #ffffff;

border-radius: 8px;

box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);

width: min(520px, calc(100vw - 2rem));

max-height: calc(100vh - 2rem);

overflow-y: auto;

padding: 0;

/* Animation d'entrée */

transform: translateY(-16px);

transition: transform 0.2s ease;

}



.overlay.est-ouverte .modale {

transform: translateY(0);

}



/* En-tête */

.modale-entete {

display: flex;

align-items: center;

justify-content: space-between;

padding: 1.25rem 1.5rem;

border-bottom: 1px solid #e5e7eb;

}



.modale-entete h2 {

margin: 0;

font-size: 1.125rem;

font-weight: 600;

}



/* Bouton de fermeture */

.btn-fermer {

background: none;

border: none;

cursor: pointer;

padding: 0.25rem;

border-radius: 4px;

color: #6b7280;

/* Zone tactile minimale recommandée : 44x44px */

min-width: 44px;

min-height: 44px;

display: flex;

align-items: center;

justify-content: center;

}



.btn-fermer:hover { background: #f3f4f6; }

.btn-fermer:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }



/* Corps */

.modale-corps { padding: 1.5rem; }

.modale-corps p { margin: 0; color: #374151; line-height: 1.6; }



/* Pied de page */

.modale-pied {

display: flex;

justify-content: flex-end;

gap: 0.75rem;

padding: 1rem 1.5rem;

border-top: 1px solid #e5e7eb;

}



/* Boutons */

.btn-principal {

background: #3b82f6;

color: #fff;

border: none;

border-radius: 6px;

padding: 0.5rem 1.25rem;

font-size: 0.9375rem;

cursor: pointer;

min-height: 44px;

}

.btn-principal:hover { background: #2563eb; }



.btn-secondaire {

background: #f3f4f6;

color: #374151;

border: 1px solid #d1d5db;

border-radius: 6px;

padding: 0.5rem 1.25rem;

font-size: 0.9375rem;

cursor: pointer;

min-height: 44px;

}

.btn-secondaire:hover { background: #e5e7eb; }



/* Respect des préférences de mouvement réduit */

@media (prefers-reduced-motion: reduce) {

.overlay, .modale { transition: none; }

}

JavaScript : ouverture, fermeture, clic extérieur et touche Échap

// Récupération des éléments du DOM

const overlay    = document.getElementById('overlay');

const btnOuvrir  = document.getElementById('btn-ouvrir');

const btnFermer  = document.getElementById('btn-fermer');

const btnAnnuler = document.getElementById('btn-annuler');



// Mémorise l'élément qui a ouvert la modale

// pour y restituer le focus à la fermeture

let declencheur = null;



/**

* Ouvre la modale et déplace le focus à l'intérieur.
* @param {HTMLElement} el - L'élément déclencheur
*/

function ouvrirModale(el) {

declencheur = el;

overlay.classList.add('est-ouverte');

overlay.setAttribute('aria-hidden', 'false');



// Déplace le focus sur le premier élément interactif

const premierFocusable = overlay.querySelector(

'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'

);

if (premierFocusable) premierFocusable.focus();

}



/**

* Ferme la modale et restitue le focus à l'élément déclencheur.
*/

function fermerModale() {

overlay.classList.remove('est-ouverte');

overlay.setAttribute('aria-hidden', 'true');



// Restitution du focus

if (declencheur) {

declencheur.focus();

declencheur = null;

}

}



// Ouverture au clic sur le bouton principal

btnOuvrir.addEventListener('click', () => ouvrirModale(btnOuvrir));



// Fermeture via le bouton de fermeture

btnFermer.addEventListener('click', fermerModale);



// Fermeture via le bouton Annuler

btnAnnuler.addEventListener('click', fermerModale);



// Fermeture au clic sur l'overlay (en dehors de la boîte)

overlay.addEventListener('click', (e) => {

// On ne ferme que si le clic est sur l'overlay lui-même,

// pas sur un enfant (la boîte de dialogue)

if (e.target === overlay) fermerModale();

});



// Fermeture à la touche Échap

document.addEventListener('keydown', (e) => {

if (e.key === 'Escape' && overlay.classList.contains('est-ouverte')) {

fermerModale();

}

});

Ce code couvre les trois modes de fermeture attendus par les utilisateurs : le bouton dédié, le clic en dehors de la boîte et la touche Échap. La restitution du focus au déclencheur est indispensable pour les utilisateurs qui naviguent au clavier.

La balise <dialog> HTML5 avec showModal() : la méthode moderne en 2026

Depuis mars 2022, l’élément <dialog> fait partie de la Baseline des navigateurs, ce qui signifie qu’il est supporté par Chrome, Firefox, Safari et Edge dans leurs versions actuelles. En 2026, c’est la méthode recommandée pour créer une fenêtre modale JavaScript.

La méthode showModal() offre gratuitement, sans une seule ligne de JavaScript supplémentaire :

Le déplacement automatique du focus à l’intérieur de la boîte
La fermeture par la touche Échap
Un fond de scène stylisable via ::backdrop
Un role="dialog" et un aria-modal="true" implicites
Le rendu en couche supérieure (top layer), sans guerre de z-index
La page en arrière-plan rendue inerte (non interactive)

Selon Jared Cunha, qui a reconstruit la modale du U.S. Web Design System sur <dialog>, la réécriture est passée d’environ 400 lignes de JavaScript à environ 38 lignes, les comportements d’accessibilité étant désormais gérés nativement par le navigateur.

Code complet avec <dialog> et showModal()

<!-- Bouton déclencheur -->

<button id="ouvrir-dialog" type="button">Ouvrir</button>



<!-- Balise dialog : le navigateur gère le backdrop, le focus et Échap -->

<dialog id="mon-dialog" aria-labelledby="dialog-titre">



<h2 id="dialog-titre">Confirmer la suppression</h2>

<p>Cette action est irréversible. Souhaitez-vous continuer ?</p>



<!--

method="dialog" ferme la boîte à la soumission du formulaire

et stocke la valeur du bouton pressé dans dialog.returnValue

-->

<form method="dialog">

<button type="submit" value="annuler">Annuler</button>

<button type="submit" value="confirmer">Confirmer</button>

</form>

</dialog>
/* Style de la boîte de dialogue */

#mon-dialog {

border: none;

border-radius: 8px;

padding: 1.5rem;

width: min(480px, calc(100vw - 2rem));

box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);

}



/* Style du fond de scène */

#mon-dialog::backdrop {

background: rgba(0, 0, 0, 0.55);

}



/* Animation d'entrée */

#mon-dialog {

opacity: 0;

transition: opacity 0.2s, overlay 0.2s allow-discrete, display 0.2s allow-discrete;

}

#mon-dialog[open] { opacity: 1; }



@starting-style {

#mon-dialog[open] { opacity: 0; }

}



@media (prefers-reduced-motion: reduce) {

#mon-dialog { transition: none; }

}
const dialog     = document.getElementById('mon-dialog');

const btnOuvrir  = document.getElementById('ouvrir-dialog');



// Ouverture : showModal() place le dialog en top layer

btnOuvrir.addEventListener('click', () => dialog.showModal());



// Fermeture au clic sur le backdrop

// (le natif ne ferme pas au clic extérieur par défaut)

dialog.addEventListener('click', (e) => {

if (e.target === dialog) dialog.close();

});



// Lecture du résultat après fermeture

dialog.addEventListener('close', () => {

if (dialog.returnValue === 'confirmer') {

console.log('Action confirmée');

// Votre logique ici

}

});
Attention : n’utilisez jamais l’attribut open pour ouvrir une modale. Il produit un dialogue non modal, sans piège de focus ni arrière-plan inerte. Appelez toujours showModal().

Nouveauté 2026 : l’attribut closedby

Mentionné à la Google I/O 2026, l’attribut closedby permet de contrôler comment un <dialog> se ferme, sans JavaScript :

<!-- Se ferme au clic extérieur, à Échap et aux gestes natifs -->

<dialog closedby="any">…</dialog>



<!-- Se ferme uniquement à Échap (comportement par défaut de showModal) -->

<dialog closedby="closerequest">…</dialog>



<!-- Ne se ferme que via le code ou un bouton interne -->

<dialog closedby="none">…</dialog>

Support en juillet 2026 : Chrome/Edge depuis la version 134, Firefox depuis la version 141, Opera depuis la version 119. Safari ne le supporte pas encore. (Source : alsacreations.com)

Bonnes pratiques d’accessibilité pour vos popups JavaScript

Une fenêtre modale inaccessible exclut les utilisateurs de lecteurs d’écran, de navigation au clavier et de technologies d’assistance. Voici les règles à respecter absolument.

Les attributs ARIA essentiels

Pour une modale construite sans <dialog> (avec un <div>) :

Attribut Valeur Rôle
role "dialog" Identifie l’élément comme une boîte de dialogue
aria-modal "true" Indique que le contenu extérieur est inerte
aria-labelledby ID du titre Donne un nom accessible à la modale
aria-describedby ID de la description Optionnel : description du contenu

Pour un <dialog> natif ouvert avec showModal(), role="dialog" et aria-modal="true" sont implicites. Seul aria-labelledby doit être ajouté manuellement.

Le piège de focus (focus trap)

Quand une modale est ouverte, la navigation au clavier (touche Tab) ne doit pas sortir de la boîte. Avec <dialog> et showModal(), ce comportement est géré nativement par le navigateur. Pour une modale custom en <div>, il faut l’implémenter manuellement :

function piegerFocus(modale) {

const selecteursFocusables = [

'a[href]', 'button:not([disabled])', 'input:not([disabled])',

'select:not([disabled])', 'textarea:not([disabled])',

'[tabindex]:not([tabindex="-1"])'

].join(', ');



const elements = Array.from(modale.querySelectorAll(selecteursFocusables));

const premier  = elements[0];

const dernier  = elements[elements.length - 1];



modale.addEventListener('keydown', (e) => {

if (e.key !== 'Tab') return;



// Shift+Tab sur le premier élément : aller au dernier

if (e.shiftKey && document.activeElement === premier) {

e.preventDefault();

dernier.focus();

}

// Tab sur le dernier élément : revenir au premier

else if (!e.shiftKey && document.activeElement === dernier) {

e.preventDefault();

premier.focus();

}

});

}

Retour du focus à la fermeture

C’est l’un des points les plus souvent oubliés. Quand la modale se ferme, le focus doit retourner sur l’élément qui l’a ouverte. Avec <dialog> et showModal(), ce comportement est automatique. Avec une modale custom, il faut mémoriser le déclencheur avant l’ouverture et appeler .focus() à la fermeture (comme montré dans le code de la section précédente).

Rendre l’arrière-plan inerte

Pour les modales custom, l’arrière-plan doit être rendu non interactif. La méthode moderne utilise l’attribut inert :

// À l'ouverture

document.querySelectorAll('body > *:not(#overlay)').forEach(el => {

el.inert = true;

});



// À la fermeture

document.querySelectorAll('[inert]').forEach(el => {

el.inert = false;

});

L’attribut inert retire les éléments de l’ordre de tabulation et les masque aux technologies d’assistance, ce qui est plus robuste qu’aria-hidden="true" seul.

Checklist accessibilité rapide

1.Le focus entre dans la modale à l’ouverture ?
2.Tab reste à l’intérieur (piège de focus actif) ?
3.Échap ferme la modale ?
4.Le focus revient au déclencheur à la fermeture ?
5.La modale a un nom accessible via aria-labelledby ?
6.Le lecteur d’écran annonce « dialogue » à l’ouverture ?
7.Le bouton de fermeture a un libellé explicite (aria-label) ?
8.L’arrière-plan est rendu inerte ?

Les déclencheurs intelligents : délai, scroll et exit intent

Une popup qui s’affiche dès le chargement de la page est à la fois agressive et contre-productive. Les déclencheurs comportementaux permettent d’afficher la modale au bon moment, ce qui améliore à la fois l’expérience utilisateur et les taux de conversion.

Déclencheur après un délai

Le plus simple : afficher la popup après X secondes. Recommandation : attendre au minimum 8 à 15 secondes pour laisser l’utilisateur commencer à lire.

// Affiche la popup après 10 secondes

// et ne la montre qu'une fois par session

function initPopupDelai(delaiMs = 10000) {

if (sessionStorage.getItem('popup-affichee')) return;



setTimeout(() => {

dialog.showModal();

sessionStorage.setItem('popup-affichee', '1');

}, delaiMs);

}



initPopupDelai(10000);

Déclencheur au scroll

Afficher la popup quand l’utilisateur a scrollé jusqu’à un certain pourcentage de la page. C’est un signal d’engagement fort.

function initPopupScroll(seuilPourcent = 50) {

if (sessionStorage.getItem('popup-affichee')) return;

let declenche = false;



function calculerPourcentageScroll() {

const h  = document.documentElement;

const b  = document.body;
const st = h.scrollTop || b.scrollTop;
const sh = h.scrollHeight || b.scrollHeight;
return (st / (sh - h.clientHeight)) * 100;

}



window.addEventListener('scroll', () => {

if (!declenche && calculerPourcentageScroll() >= seuilPourcent) {

declenche = true;

dialog.showModal();

sessionStorage.setItem('popup-affichee', '1');

window.removeEventListener('scroll', arguments.callee);

}

}, { passive: true });

}



initPopupScroll(50);

Déclencheur exit intent (intention de sortie)

L’exit intent détecte que l’utilisateur s’apprête à quitter la page. Sur desktop, on surveille le mouvement de la souris vers le haut du viewport. Sur mobile, l’événement mouseleave n’existe pas : on utilise un scroll rapide vers le haut comme signal de substitution.

function initExitIntent(callback, options = {}) {

const {

sensibilite = 0,       // px depuis le bord supérieur

delaiMinMs  = 5000,    // délai minimum avant activation

scrollDelta = 60,      // px de scroll rapide vers le haut (mobile)

uneSeuleFois = true,

} = options;



let declenche    = false;

let dernierScrollY = window.scrollY;

const debut      = Date.now();



const declencher = () => {

if (declenche) return;

if (Date.now() - debut < delaiMinMs) return;

if (uneSeuleFois) declenche = true;

callback();

};



// Desktop : mouvement de souris vers le haut

document.addEventListener('mouseout', (e) => {

if (!e.relatedTarget && e.clientY <= sensibilite) declencher();

});



// Mobile : scroll rapide vers le haut

window.addEventListener('scroll', () => {

const y = window.scrollY;

if (dernierScrollY - y > scrollDelta) declencher();

dernierScrollY = y;

}, { passive: true });

}



// Utilisation

initExitIntent(() => {

if (!sessionStorage.getItem('exit-popup-affichee')) {

dialog.showModal();

sessionStorage.setItem('exit-popup-affichee', '1');

}

}, { delaiMinMs: 5000 });
Bonne pratique : stockez toujours l’état d’affichage dans sessionStorage ou localStorage pour ne pas répéter la popup à chaque page ou à chaque visite.

Popups et SEO : les règles pour ne pas être pénalisé par Google

Les popups JavaScript ont un impact direct sur votre référencement naturel si elles sont mal configurées. Google applique des sanctions précises, qu’il est indispensable de connaître.

La pénalité interstitiels intrusifs (depuis 2017)

En janvier 2017, Google a déployé une mise à jour ciblant les interstitiels intrusifs sur mobile. Trois configurations déclenchent cette pénalité :

1.Une popup qui couvre la totalité ou la majorité du contenu principal dès l’arrivée sur la page depuis les résultats de recherche
2.Un interstitiel que l’utilisateur doit obligatoirement fermer pour accéder au contenu
3.Un élément de mise en page qui pousse le contenu principal sous la ligne de flottaison

Les sites pénalisés peuvent voir leur trafic mobile chuter de 15 à 40 % selon les observations de terrain.

La mise à jour anti-spam de juin 2026

En juin 2026, Google a renforcé son traitement des popups trompeuses dans le cadre de son update anti-spam. Les popups qui simulent une urgence artificielle, masquent délibérément le bouton de fermeture ou empêchent l’accès au contenu peuvent désormais déclencher une action algorithmique, indépendamment du support (mobile ou desktop).

L’impact sur les Core Web Vitals

Les popups dégradent deux métriques clés :

CLS (Cumulative Layout Shift) : une popup injectée après le rendu initial qui décale le contenu génère un score CLS élevé, signal négatif pour Google.
INP (Interaction to Next Paint) : un script de popup lourd qui bloque le thread principal augmente le temps de réponse aux interactions.

Ce que Google autorise explicitement

Certaines popups ne sont pas pénalisées :

Les bannières de consentement aux cookies (RGPD)
Les vérifications d’âge légalement obligatoires
Les écrans de connexion pour du contenu privé
Les bannières de petite taille, facilement fermables, n’occupant pas une surface déraisonnable

Les règles à respecter pour rester dans les clous

Déclencher la popup au minimum 8 à 15 secondes après l’arrivée, ou au scroll (jamais au chargement immédiat)
Limiter la surface à 30 à 40 % du viewport sur mobile
Afficher un bouton de fermeture clairement visible, d’au moins 44×44 pixels
Ne pas réafficher la popup dans la même session après fermeture
Tester systématiquement sur un vrai appareil mobile

Consentement RGPD et popups

Si votre popup collecte des données personnelles (email, prénom, etc.), elle doit respecter le RGPD :

Informer clairement l’utilisateur de l’usage de ses données
Proposer un lien vers la politique de confidentialité
Ne pas pré-cocher des cases de consentement
Permettre le refus aussi facilement que l’acceptation
Ne pas conditionner l’accès au contenu au remplissage du formulaire

Tableau comparatif des méthodes

Méthode Personnalisation Accessibilité Complexité Recommandée en 2026
alert() / confirm() / prompt() Aucune Basique Très faible Non (débogage uniquement)
Modale custom HTML/CSS/JS Totale À implémenter Moyenne Oui (si besoin de contrôle total)
<dialog> + showModal() Totale Native Faible Oui (méthode recommandée)
<dialog> + attribut open Totale Partielle Faible Non (non modal, pas de focus trap)
window.open() Limitée Mauvaise Faible Non (bloqué par les navigateurs)

Pour aller plus loin côté technique, consultez nos guides pour inspecter une page web et afficher le symbole copyright en HTML. Et si votre objectif derrière ce pop-up est de capter des prospects, Djaboo centralise ensuite vos contacts dans un CRM et transforme vos devis en factures en un clic.

FAQ : 5 questions fréquentes sur les popups JavaScript

1. Quelle est la différence entre `showModal()` et `show()` sur un `<dialog>` ?

showModal() ouvre la boîte en mode modal : le reste de la page devient inerte, le focus est piégé à l’intérieur et la touche Échap ferme automatiquement la boîte. show() ouvre un dialogue non modal : l’utilisateur peut continuer à interagir avec la page, le focus n’est pas piégé. Utilisez showModal() pour les confirmations, formulaires importants et toute interruption nécessitant une réponse. Utilisez show() pour les panneaux d’aide contextuelle ou les notifications non bloquantes.

2. Dois-je ajouter un piège de focus (focus trap) à un `<dialog>` natif ouvert avec `showModal()` ?

Non. Le navigateur gère déjà le confinement du focus via la mise en top layer et l’inertie de l’arrière-plan. Ajouter un piège de focus JavaScript sur un <dialog> natif est non seulement inutile, mais peut supprimer la capacité des utilisateurs clavier à atteindre les contrôles du navigateur (barre d’adresse, onglets). Le focus trap manuel ne s’applique qu’aux modales construites avec des <div> personnalisés.

3. Comment empêcher le scroll de la page derrière une modale ouverte ?

Avec <dialog> et showModal(), le navigateur ne bloque pas le scroll de la page par défaut. La solution CSS la plus propre :

:root:has(dialog[open]) {

overflow: hidden;

scrollbar-gutter: stable; /* évite le saut de mise en page */

}

Sur iOS Safari, overflow: hidden sur le body est peu fiable. La solution plus robuste consiste à capturer window.scrollY à l’ouverture et à appliquer position: fixed avec un top négatif correspondant.

4. L’exit intent fonctionne-t-il sur mobile ?

Pas directement, car les appareils tactiles ne génèrent pas d’événements mouseleave. Les alternatives pour mobile sont le déclencheur au scroll (scroll rapide vers le haut après avoir scrollé au moins 100 px vers le bas) et le déclencheur par inactivité (aucune interaction pendant 20 à 30 secondes). Ces signaux sont moins précis que l’exit intent desktop, mais suffisamment fiables pour être utiles. Sur mobile, un déclencheur au scroll à 60 à 70 % de la page reste la meilleure alternative.

5. Comment éviter que la popup s’affiche à chaque visite ?

Utilisez sessionStorage pour ne pas répéter la popup dans la même session, et localStorage pour une suppression sur une durée plus longue (7 jours, 14 jours, etc.) :

// Ne montre la popup qu'une fois par semaine

function peutAfficherPopup(cle, joursIntervalle = 7) {

const stockage = localStorage.getItem(cle);

if (stockage) {

const joursEcoules = (Date.now() - Number(stockage)) / (1000 * 60 * 60 * 24);

if (joursEcoules < joursIntervalle) return false;

}

localStorage.setItem(cle, String(Date.now()));

return true;

}



if (peutAfficherPopup('ma-popup', 7)) {

dialog.showModal();

}

Maîtriser les popups JavaScript, c’est bien plus que connaître trois méthodes de déclenchement. C’est comprendre les implications en termes d’accessibilité, de performance et de référencement, et choisir la bonne approche selon le contexte. La balise <dialog> avec showModal() est aujourd’hui la solution la plus complète et la moins coûteuse en code : elle gère nativement ce qu’il fallait autrefois implémenter manuellement en des centaines de lignes. Pour les modales plus complexes (animations élaborées, systèmes de design, contraintes d’empilement spécifiques), une implémentation custom reste pertinente, à condition de respecter scrupuleusement les règles d’accessibilité et les directives SEO de Google.

Si vous gérez des projets web et avez besoin d’un outil pour centraliser vos clients, vos équipes et vos workflows, Djaboo est une solution CRM tout-en-un conçue pour les TPE et PME, sans compétences techniques requises.

5/5 - (562 votes)

Vous êtes débordé par votre gestion ?

Djaboo s’en charge pour vous !