FE Interview Hub
Browser InternalsIntermediate

Explain event delegation, capturing, and bubbling in the browser

AI Practice

Three phases of event propagation

When you click a DOM element, the event travels in three phases — it doesn't just fire on that one element.

Window
  └─ Document
       └─ <html>
            └─ <body>
                 └─ <div>  ← 1. Capture phase (top → target)
                      └─ <button>  ← 2. Target phase
                 └─ <div>  ← 3. Bubble phase (target → top)

1. Capture phase

The event travels down from window to the target. Pass true as the third argument to addEventListener to listen in this phase.

parent.addEventListener('click', handler, true); // capture

2. Target phase

The event reaches the element that was actually clicked. Both capture and bubble listeners on this element run.

3. Bubble phase

The event travels back up from the target to window. This is the default phase for addEventListener.

child.addEventListener('click', handler);        // bubble (default)
child.addEventListener('click', handler, false); // same

Not all events bubble: focus, blur, load, scroll do not bubble by default.

Stopping propagation

  • e.stopPropagation() — stops the event from travelling further up (or down).
  • e.stopImmediatePropagation() — stops propagation and also prevents any remaining listeners on the same element.
child.addEventListener('click', (e) => {
  e.stopPropagation(); // parent won't receive the event
});

Event delegation

Because events bubble, you can attach a single listener on a parent to handle events from all its children.

Benefits

  • Fewer event listeners → less memory
  • Dynamically added children are automatically covered — no need to re-bind

Example

<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
</ul>
// Avoid: binding to every <li>
document.querySelectorAll('#list li').forEach(li => {
  li.addEventListener('click', handleClick);
});

// Better: delegate to the parent
document.getElementById('list').addEventListener('click', (e) => {
  if (e.target.tagName === 'LI') {
    console.log('Clicked:', e.target.textContent);
  }
});

Preventing default behaviour

e.preventDefault() cancels the browser's built-in reaction (e.g. following a link, submitting a form). This is separate from stopping propagation.

Summary

Concept Direction How to listen
Capture window → target addEventListener(_, _, true)
Target Both phases fire
Bubble target → window addEventListener(_, _) (default)
Delegation Use bubbling to handle children on a parent

✦ AI Mock Interview

Type your answer and get instant AI feedback

Sign in to use AI scoring