Event Delegation in JavaScript: How It Works + Performance Patterns

A deep, interview-ready guide to event delegation: bubbling/capturing, delegation patterns, performance benefits, common pitfalls, and clean implementations for real UIs.

F

Frontend Interview Team

February 08, 2026

~18 min
Event Delegation in JavaScript: How It Works + Performance Patterns

Event delegation is a high-signal topic because it tests your understanding of:

30‑second interview answer

Event delegation means attaching one event listener on a parent and handling events for children using bubbling. It’s faster (fewer listeners), uses less memory, and works for dynamically added elements. The common pattern is e.target.closest(selector) to find the intended target.

Key points

  • Delegation relies on bubbling.
  • One listener beats thousands.
  • Use closest to match actionable elements.
  • Be careful with stopPropagation and nested handlers.
  • DOM events
  • propagation
  • performance
  • dynamic UIs

1) The problem delegation solves

If you attach listeners to many items:

document.querySelectorAll('.item').forEach((el) => {
  el.addEventListener('click', () => {
    // ...
  });
});

Problems:

  • many listeners (memory)
  • expensive setup
  • doesn’t handle dynamically added items unless you re-bind

2) Event propagation: capturing vs bubbling

Events travel in phases:

  1. capturing (top → down)
  2. target
  3. bubbling (target → up)

Most events bubble.

parent.addEventListener('click', handler); // default uses bubbling
parent.addEventListener('click', handler, true); // capturing

3) Delegation pattern (core idea)

Attach one listener to a common ancestor and decide what was clicked.

const list = document.querySelector('#list');
 
list.addEventListener('click', (e) => {
  const item = e.target.closest('.item');
  if (!item) return;
 
  console.log('Clicked item id:', item.dataset.id);
});

Key tool: closest().


4) Why delegation improves performance

  • fewer listeners
  • less memory
  • faster startup
  • supports dynamic children naturally

Delegation is especially useful for:

  • tables
  • lists
  • menus
  • infinite scroll feeds

5) Common pitfalls

A) e.target vs e.currentTarget

  • target = the deepest element clicked
  • currentTarget = element with the listener

B) stopPropagation()

If a child calls stopPropagation, delegated listener won’t run.

C) Non-bubbling events

Some events don’t bubble (e.g., focus), but you can use focusin.


6) Delegation with multiple actions

container.addEventListener('click', (e) => {
  const btn = e.target.closest('button[data-action]');
  if (!btn) return;
 
  const action = btn.dataset.action;
  const row = btn.closest('[data-id]');
  const id = row?.dataset.id;
 
  if (action === 'delete') deleteRow(id);
  if (action === 'edit') editRow(id);
});

7) Interview Q&A

Q: Why use event delegation?

  • performance + works with dynamic elements.

Q: How do you find the element you care about?

  • e.target.closest(selector).

Q: What’s bubbling vs capturing?

  • bubbling goes from target up; capturing goes from root down.

Summary checklist

  • I can explain bubbling/capturing.
  • I can implement delegation with closest.
  • I can explain why it helps performance.
  • I know stopPropagation can break delegation.

Summary

  • Delegation = one listener on parent + detect targets.
  • It reduces listeners and handles dynamic UI.
  • Use closest and understand propagation.