Chapter 10 โ€” Event Bubbling, Capturing & Delegation

๐Ÿ“– Definitions

  • Event bubbling โ€” an event triggered on a child propagates upward through its ancestors.
  • Event capturing โ€” the opposite direction; from root down to the target.
  • Event delegation โ€” attaching one listener on a parent and relying on bubbling to handle events from many descendants.

๐Ÿ” Explanation

When you click an element, the event travels through three phases:

  1. Capture phase โ€” root โ†’ target (top-down).
  2. Target phase โ€” at the actual clicked element.
  3. Bubble phase โ€” target โ†’ root (bottom-up).

By default, addEventListener listens in the bubble phase. Pass true (or { capture: true }) to listen in the capture phase.

๐Ÿ’ป Code Example โ€” Bubbling

html
<div id="outer">
  <div id="inner">
    <button id="btn">Click me</button>
  </div>
</div>
document.getElementById("outer").addEventListener("click", () => console.log("outer"));
document.getElementById("inner").addEventListener("click", () => console.log("inner"));
document.getElementById("btn").addEventListener("click",   () => console.log("btn"));

// Clicking the button logs: btn โ†’ inner โ†’ outer  (bubbling)

๐Ÿ’ป Code Example โ€” Capturing

document.getElementById("outer").addEventListener("click",
  () => console.log("outer"), true);  // capture
document.getElementById("inner").addEventListener("click",
  () => console.log("inner"), true);
document.getElementById("btn").addEventListener("click",
  () => console.log("btn"));

// Output on click: outer โ†’ inner โ†’ btn  (capture goes top-down)

๐Ÿ’ป Code Example โ€” Stopping Propagation

document.getElementById("btn").addEventListener("click", (e) => {
  console.log("btn");
  e.stopPropagation();          // prevents outer/inner from firing
});

// Now clicking the button logs only: btn

e.stopImmediatePropagation() also prevents other listeners on the same element.

๐Ÿ’ป Code Example โ€” Event Delegation

html
<ul id="list">
  <li data-id="1">One</li>
  <li data-id="2">Two</li>
  <li data-id="3">Three</li>
</ul>
// One listener handles all <li> clicks โ€” even ones added later
document.getElementById("list").addEventListener("click", (e) => {
  const li = e.target.closest("li");
  if (!li) return;
  console.log("Clicked id:", li.dataset.id);
});

// Dynamically add โ€” still works without re-binding
document.getElementById("list").insertAdjacentHTML(
  "beforeend", `<li data-id="4">Four</li>`
);

Benefits of Delegation

  • 1 listener instead of N โ†’ less memory.
  • Works for elements added after the listener is attached.
  • Easier to clean up.

๐Ÿ’ป Code Example โ€” preventDefault

document.querySelector("a").addEventListener("click", (e) => {
  e.preventDefault();           // stop navigation
  console.log("Link clicked but blocked");
});

document.querySelector("form").addEventListener("submit", (e) => {
  e.preventDefault();           // stop full-page form submit
  // ...submit via fetch
});

๐Ÿ’ป Code Example โ€” Once / Passive Options

button.addEventListener("click", handler, { once: true }); // auto-removes after first call

window.addEventListener("scroll", onScroll, { passive: true });
// `passive: true` tells the browser the handler will not call preventDefault,
// allowing it to scroll without waiting โ†’ smoother performance.

๐ŸŒ Real-World Impact

  • In React, synthetic events are delegated to the root container automatically.
  • Tables, lists, and large grids should use event delegation for performance.
  • passive listeners are crucial for scroll/touch performance on mobile.

๐ŸŽฏ Likely Interview Questions

  1. Event bubbling vs capturing.
  2. What is event delegation and why use it?
  3. Difference between stopPropagation and preventDefault?
    • stopPropagation โ€” stops the event from traveling further along the chain.
    • preventDefault โ€” cancels the browser's default action (navigation, form submit, etc.).
  4. How does React handle events under the hood? โ€” Synthetic event system, delegated to the root.

โ† Equality | Index | Next: Array Methods โ†’