À propos de ce cours
Chapitre 13 — Les événements · 13.4 La propagation des événements : bubbling, capture et stopPropagation
• comprendre que l'événement traverse les ancêtres de l'élément ;
• connaître les trois phases : capture, cible, remontée ;
• écouter pendant la capture avec
{ capture: true }; • arrêter la propagation avec
stopPropagation(), et savoir quand l'éviter.1. Un clic concerne plusieurs éléments
Quand on clique sur un bouton placé dans un paragraphe, lui-même dans une section, on clique aussi sur le paragraphe,
la section, le body… L'événement ne reste donc pas sur la cible : il voyage à travers tous ses ancêtres. C'est la propagation.

Figure 1 — Le voyage d'un événement.
📄 l4_propagation.html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
<style>
.boite { padding: 18px; border-radius: 10px; border: 2px solid; }
#grandparent { background: #e8f5e9; border-color: #2e7d32; }
#parent { background: #fff3e0; border-color: #ef6c00; }
#enfant { background: #e3f2fd; border-color: #1565c0; cursor: pointer; text-align: center; font-weight: 700; }
</style>
</head>
<body>
<div class="boite" id="grandparent">grand-parent
<div class="boite" id="parent">parent
<div class="boite" id="enfant">enfant : cliquez-moi</div>
</div>
</div>
<p><label class="choix"><input type="checkbox" id="stop"> Appeler stopPropagation() dans « parent » (phase de remontée)</label></p>
<pre id="sortie"></pre>
<script>
const sortie = document.querySelector("#sortie");
let n = 0;
const phases = { 1: "CAPTURE", 2: "CIBLE", 3: "REMONTÉE" };
for (const id of ["grandparent", "parent", "enfant"]) {
const el = document.getElementById(id);
// Troisième argument true (ou { capture: true }) : écouter pendant la descente
el.addEventListener("click", (ev) => {
sortie.textContent += `${++n}. ${id.padEnd(12)} ${phases[ev.eventPhase]}\n`;
}, true);
// Par défaut : écouter pendant la remontée (bubbling)
el.addEventListener("click", (ev) => {
sortie.textContent += `${++n}. ${id.padEnd(12)} ${phases[ev.eventPhase]}\n`;
if (id === "parent" && document.querySelector("#stop").checked) {
ev.stopPropagation();
sortie.textContent += " ⛔ stopPropagation() : grand-parent ne sera pas prévenu\n";
}
});
}
document.querySelector("#enfant").addEventListener("mousedown", () => { sortie.textContent = ""; n = 0; });
</script>
</body>
</html>

Un clic sur « enfant » : chaque élément écoute en capture et en remontée
- Capture: l'événement descend de
windowjusqu'au parent de la cible. Seuls les écouteurs ajoutés avec{ capture: true }(outrue) sont appelés. - Cible: l'événement atteint l'élément cliqué.
- Remontée (bubbling) : l'événement remonte jusqu'à
window. C'est pendant cette phase que sont appelés les écouteurs ordinaires.
ev.eventPhase vaut 1, 2 ou 3 selon la phase en cours.
2. Arrêter la propagation
En cochant la case, l'écouteur de « parent » appelle ev.stopPropagation(): le grand-parent n'est plus prévenu.

Le voyage s'arrête au parent pendant la remontée
Utilisez
stopPropagation() avec parcimonie. Un code ailleurs dans la page (une fenêtre qui se ferme au clic à l'extérieur,
un outil de statistiques, la délégation) peut compter sur le fait que l'événement remonte. Souvent, il vaut mieux tester ev.target dans l'écouteur
du parent. Et stopPropagation() n'empêche pas l'action par défaut du navigateur : c'est le rôle de preventDefault() (leçon 13.5).// Fermer un menu quand on clique en dehors : on a BESOIN que le clic remonte jusqu'à document
document.addEventListener("click", (ev) => {
if (!menu.contains(ev.target)) menu.hidden = true;
});
// Certains événements ne remontent pas : focus, blur, mouseenter, mouseleave, load…
// Leurs équivalents qui remontent : focusin, focusout, mouseover, mouseout
• Un événement descend (capture), atteint la cible, puis remonte (bubbling) à travers les ancêtres.
• Écouteurs ordinaires : phase de remontée ;
{ capture: true }: phase de descente.•
ev.stopPropagation() arrête le voyage ; à utiliser rarement.•
focus, blur, mouseenter, mouseleave ne remontent pas.🛠 Activité 4 — Une carte cliquable
Mission : Créez une « carte produit » cliquable qui ouvre une fiche détaillée, et qui contient un bouton « Ajouter au panier » et un lien « Partager ».
Questions pour avancer (répondez dans votre compte rendu, puis appliquez dans votre projet) :
- Mettez un écouteur
clicksur la carte et un autre sur le bouton. Cliquez sur le bouton : combien d'actions sont déclenchées ? Pourquoi ? - Corrigez d'abord avec
stopPropagation()dans le bouton. Puis, autrement, en testantev.target.closest("button")dans l'écouteur de la carte. Quelle solution préférez-vous ? Pourquoi ? - Ajoutez un menu « … » qui s'ouvre au clic et se ferme dès que l'on clique ailleurs dans la page. Si le bouton du panier utilise
stopPropagation(), le menu se ferme-t-il encore quand on clique sur ce bouton ? Qu'en concluez-vous ? - Écoutez
clicksurdocumenten phase de capture et affichezev.target. Cet écouteur s'exécute-t-il avant ou après celui de la carte ? À quoi peut servir un tel écouteur ? - Mettez des écouteurs
focuspuisfocusinsur la carte. Lequel est déclenché quand le bouton intérieur reçoit le focus ? Pourquoi ?
Je vérifie / je rends :
- ☐ La carte et un schéma de la propagation pour un clic sur le bouton.
← 13.3 Les événements de la souris et du clavier13.5 preventDefault et la délégation d'événements →