Event DelegationLoading saved progress…

Event Delegation

Instead of attaching a listener to every button in a list, event delegation attaches one listener to a common ancestor and lets events bubble up to it. When the event arrives, you check whether it came from (or through) an element matching a selector, and handle it there. It's the classic pattern behind jQuery's .on(parent, selector, handler) and React's synthetic events — cheaper (one listener, not N) and it automatically covers elements added after you wired it up.

Implement eventDelegation(root, eventType, selector, handler). Attach a single listener to root; when an eventType event bubbles up from a descendant matching selector, call handler(event, matchedElement) with this set to the matched element. Return a cleanup function.

Signature

function eventDelegation(root, eventType, selector, handler) {
  // returns a cleanup function that removes the listener
}

Examples

// One listener handles every .item, including ones added later.
const off = eventDelegation(list, 'click', '.item', (e, el) => {
  console.log('clicked item', el.dataset.id);
});
off(); // stop listening

Notes

  • One listener on the root — rely on bubbling; don't attach per-descendant.
  • closest finds the match — the real event.target may be a child of the selector match (e.g. an icon inside a button); use event.target.closest(selector) to walk up.
  • Scope to root — only handle matches that are actually inside root (root.contains(matched)).
  • Dynamic — because the listener is on the ancestor, descendants added after wiring are handled automatically. Return a cleanup that calls removeEventListener.
Loading editor…