Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
JavaScript

La propagation des événements : bubbling, capture et stopPropagation

La propagation des événements : bubbling, capture et stopPropagation. Dans cette leçon du cours JavaScript, vous apprendrez à : comprendre que l'événement traverse les ancêtres de l'élément ; connaîtr...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

La propagation des événements : bubbling, capture et stopPropagation. Dans cette leçon du cours JavaScript, vous apprendrez à : 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.

Chapitre 13 — Les événements · 13.4 La propagation des événements : bubbling, capture et stopPropagation

🎯 Objectifs de la leçon
• 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.

Les trois phases de la propagation d'un événement
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
Un clic sur « enfant » : chaque élément écoute en capture et en remontée

  1. Capture: l'événement descend de window jusqu'au parent de la cible. Seuls les écouteurs ajoutés avec { capture: true } (ou true) sont appelés.
  2. Cible: l'événement atteint l'élément cliqué.
  3. 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.

Propagation arrêtée au niveau du parent
Le voyage s'arrête au parent pendant la remontée

⚠️ Attention
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
ℹ️ À retenir
• 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) :

  1. Mettez un écouteur click sur la carte et un autre sur le bouton. Cliquez sur le bouton : combien d'actions sont déclenchées ? Pourquoi ?
  2. Corrigez d'abord avec stopPropagation() dans le bouton. Puis, autrement, en testant ev.target.closest("button") dans l'écouteur de la carte. Quelle solution préférez-vous ? Pourquoi ?
  3. 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 ?
  4. Écoutez click sur document en phase de capture et affichez ev.target. Cet écouteur s'exécute-t-il avant ou après celui de la carte ? À quoi peut servir un tel écouteur ?
  5. Mettez des écouteurs focus puis focusin sur 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 →