Intermediate⏱️ 9 min📘 Topic 9 of 13

🖱️ JavaScript Events — Listeners, Bubbling & Delegation

Master JavaScript events — addEventListener, event bubbling, capturing, delegation and preventDefault. Practical examples and the interview questions companies ask.

Events are how JavaScript reacts to the world — clicks, keypresses, form submits, mouse moves, page loads.

👂 The pattern

element.addEventListener('click', (event) => {
  console.log('Clicked!', event.target);
});

🌊 Bubbling vs Capturing

When you click a button, the event travels:

  1. Capture phase: top (window) → target
  2. Target phase: the clicked element
  3. Bubble phase: target → top

By default, listeners fire in the bubble phase. Pass { capture: true } for the capture phase.

🎯 Event delegation

Attach ONE listener to a parent and use event.target to detect which child was hit. Crucial for dynamic lists.

🛑 Stopping behavior

  • event.preventDefault() — stop default action (form submit, link navigation)
  • event.stopPropagation() — stop the event from bubbling further

💻 Code Examples

Event delegation on a list

document.querySelector('#todo-list')
  .addEventListener('click', (e) => {
    if (e.target.matches('.delete')) {
      e.target.closest('li').remove();
    }
  });
Output:
One listener handles deletes for any current AND future <li> in the list.

preventDefault on a form

form.addEventListener('submit', (e) => {
  e.preventDefault();
  // ... handle via fetch instead
});
Output:
Form won't reload the page on submit.

⚠️ Common Mistakes

  • Adding a listener inside a loop for every item — leaks memory and slows things down. Use delegation.
  • Forgetting to remove listeners on long-lived elements — leads to leaks. Use AbortController for clean removal.
  • Confusing event.target (what was clicked) with event.currentTarget (where the listener is attached).

🎯 Interview Questions

Real questions asked at top product and service-based companies.

Q1.What's event bubbling?Beginner
After firing on the target, an event travels up through its ancestors (parent → grandparent → … → window), triggering listeners along the way. This lets parents react to child events.
Q2.What is event delegation?Intermediate
Attaching a single listener to a common ancestor and inspecting `event.target` to handle child events. Reduces memory and works with elements added later dynamically.
Q3.Difference between event.target and event.currentTarget?Intermediate
target = the actual element clicked. currentTarget = the element the listener is attached to. In delegation they differ; otherwise they match.
Q4.Difference between preventDefault and stopPropagation?Intermediate
preventDefault cancels the browser's default behavior (form submit, link navigation, scroll). stopPropagation stops the event from bubbling/capturing to other listeners. They're independent.
Q5.How do you remove an event listener cleanly?Advanced
Either keep a named reference (`el.removeEventListener('click', handler)`) or use AbortController: `el.addEventListener('click', fn, { signal: ctrl.signal })` and call `ctrl.abort()` to remove all at once.

🧠 Quick Summary

  • addEventListener is the modern API — supports multiple listeners.
  • Events bubble by default; capture phase is opt-in.
  • Delegation = one parent listener for many children.
  • preventDefault blocks default behavior; stopPropagation blocks bubbling.
  • Use AbortController for clean teardown.