What it IS:

  • You put ONE event listener on a parent element
  • When ANY child gets clicked, the event bubbles up to that parent
  • The parent handler uses event.target to see which specific child was actually clicked
  • Based on that, you decide what to do

Why it Works:

  • Bubbling means child events automatically reach the parent
  • event.target never changes - it always points to the original clicked element
  • So parent can “see” what child was clicked without having listeners on each child

The Mechanics:

javascript

// Instead of:
child1.onclick = doSomething;
child2.onclick = doSomething;
child3.onclick = doSomething;
// (Repeat for 100 children)
 
// You do:
parent.onclick = function(e) {
  // e.target tells you which child was actually clicked
  doSomething(e.target);
};

Real Behavior:

  • Click child → bubbles to parent → parent asks “who got clicked?” → acts on that specific child
  • Add new children? They automatically work (parent catches their bubbles too)
  • Remove children? No cleanup needed (no individual listeners to remove)

Main Interview Points:

1. Uses event.target to find what was actually clicked:

javascript

table.onclick = function(event) {
    let clicked = event.target; // The actual element clicked
    if (clicked.tagName === 'TD') {
        highlight(clicked);
    }
};
// Works for any number of cells - 10 or 10,000

2. Handles dynamic content automatically:

javascript

// Add new buttons later - delegation still works!
container.innerHTML += '<button>New Button</button>';
// No need to attach new event listeners

3. Common pattern with data attributes:

javascript

// HTML: <button data-action="save">Save</button>
menu.onclick = function(event) {
    let action = event.target.dataset.action; // "save"
    if (action) {
        this[action](); // Calls this.save()
    }
};

Interview Gotchas:

Gotcha #1: Click might hit nested elements

javascript

// Problem: <td><strong>Text</strong></td>
// Click on <strong> gives event.target = <strong>, not <td>
 
// Solution: Use closest()
let td = event.target.closest('td'); // Walks up to find <td>

Gotcha #2: Not all events bubble

  • Focus/blur events don’t bubble (use focusin/focusout instead)
  • Some events like scroll have weird bubbling

Gotcha #3: Performance consideration

javascript

// Handler fires for EVERY click in container
container.onclick = function(event) {
    // This runs even for clicks you don't care about
    if (event.target.matches('.important')) {
        // Only handle important clicks
    }
};

Bottom Line:

Event delegation = one parent listener catches events from many children. Perfect for dynamic content, saves memory, reduces code. Just remember to check event.target and use closest() for nested elements. It’s the difference between hiring 100 security guards vs. 1 smart one with cameras.