Skip to content
  • Events: Bubbling, Capturing & Delegation · JavaScript

Events: Bubbling, Capturing & Delegation

LearnViaNotes @learnvianotes· 2 min read

A DOM event travels down the tree (capture), hits the target, then travels back up (bubble). Delegation uses the bubbling phase to handle many elements with one listener.

The three phases

  1. Capture

    From window down through each ancestor to the target's parent. Listeners registered with { capture: true } run here.

  2. Target

    Listeners on the element itself.

  3. Bubble

    Back up from the parent to window. Default listeners run here. (focus, blur, mouseenter don't bubble.)

JavaScript
// <div id="outer"><button id="inner">Click</button></div>
outer.addEventListener("click", () => console.log("outer capture"), { capture: true });
outer.addEventListener("click", () => console.log("outer bubble"));
inner.addEventListener("click", () => console.log("button"));
// clicking the button logs: outer capture → button → outer bubble
Output
JavaScript (browser, not run)

Event object essentials

MemberPurpose
event.targetThe element that was actually clicked (deepest)
event.currentTargetThe element whose listener is running now
event.preventDefault()Cancel the default action (follow link, submit form)
event.stopPropagation()Stop travelling to further ancestors
event.stopImmediatePropagation()Also skip remaining listeners on the same element
{ once: true }, { passive: true }, { signal }Auto-remove; promise not to preventDefault (smooth scrolling); remove via AbortController

Event delegation

JavaScript
// One listener for any number of (even future) <li> items:
document.querySelector("#todo").addEventListener("click", (event) => {
  const item = event.target.closest("li[data-id]");
  if (!item || !event.currentTarget.contains(item)) return;
  if (event.target.matches(".delete")) item.remove();
  else item.classList.toggle("done");
});
Output
JavaScript (browser, not run)

Runnable: EventTarget and CustomEvent

EventTarget, Event and CustomEvent also exist in Node, so the listener API itself can be demonstrated (there is no tree, so no bubbling):

JavaScript
const cart = new EventTarget();
const controller = new AbortController();

cart.addEventListener("add", (e) => console.log("added", e.detail.item), { signal: controller.signal });
cart.addEventListener("add", () => console.log("first add only"), { once: true });
cart.addEventListener("checkout", (e) => {
  e.preventDefault();
  console.log("checkout blocked by listener");
});

cart.dispatchEvent(new CustomEvent("add", { detail: { item: "book" } }));
cart.dispatchEvent(new CustomEvent("add", { detail: { item: "pen" } }));
controller.abort();                                     // removes the first listener
cart.dispatchEvent(new CustomEvent("add", { detail: { item: "lamp" } }));

const ok = cart.dispatchEvent(new Event("checkout", { cancelable: true }));
console.log("dispatch returned", ok);
Output
added book
first add only
added pen
checkout blocked by listener
dispatch returned false

What is event delegation?

target vs currentTarget?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report