foundation

Event delegation

Attach fewer listeners by handling bubbled events at stable ancestors and filtering with targets or closest.

Event delegation attaches a listener to a stable ancestor and handles events from matching descendants. It works because many events bubble. Delegation reduces listener churn in dynamic lists, but robust code must inspect event.target, use closest, validate containment, and account for events that do not bubble.

					list.addEventListener('click', (event) => {
  const row = event.target.closest('[data-row]');
  if (!row || !list.contains(row)) return;
  activateRow(row);
});
				

`mouseenter`, `focus`, and some pointer events do not bubble — delegation patterns differ. `currentTarget` is the element with the listener; `target` is where the event originated.

On interviews: expect questions about large lists, dynamic nodes, target versus currentTarget, and why focus or mouseenter need special handling.

Common pitfalls: delegating too high in the tree can hide ownership. Forgetting containment checks can match nested unrelated widgets.

The trade-off is convenience versus control — pick the mechanism that matches your coupling and performance budget.

Checklist:

  • Choose a stable ancestor with clear ownership.
  • Filter with closest and contains.
  • Know non-bubbling events.
  • Avoid one giant document-level handler for everything.