JavaScript Event Bubbling and Delegation


JavaScript Event Bubbling and Delegation Interview with follow-up questions

1. What is event bubbling in JavaScript?

Event bubbling is the phase where an event, after reaching its target, travels upward through the target's ancestors — firing any matching handlers on each parent in turn until it reaches document/window. So a click on a inside a `<div>` inside fires the button's handler first, then the div's, then body's.

document.body.addEventListener('click', () =&gt; console.log('body'));
div.addEventListener('click', () =&gt; console.log('div'));
button.addEventListener('click', () =&gt; console.log('button'));
// Clicking the button logs: button → div → body

Bubbling is what makes event delegation possible — one listener on a parent can handle clicks on any descendant.

Gotchas interviewers expect:

  • Stop it with event.stopPropagation().
  • Some events don't bubblefocus, blur, load, mouseenter/mouseleave. Use the bubbling equivalents focusin/focusout, or listen in the capturing phase with { capture: true }.
↑ Back to top

Follow-up 1

How can we stop event bubbling?

To stop event bubbling, you can use the event.stopPropagation() method. This method prevents the event from triggering on parent elements after it has been triggered on the current element. Here's an example:

const button = document.getElementById('button');

button.addEventListener('click', function(event) {
  event.stopPropagation();
  // Event handling code
});

Follow-up 2

Can you provide an example of event bubbling?

Sure! Here's an example:

<div>
  <div>
    Click me
  </div>
</div>

Follow-up 3

What is the difference between event bubbling and event capturing?

Event bubbling and event capturing are two different phases of event propagation in JavaScript.

Event bubbling is the default behavior where an event triggered on a nested element is first handled by the innermost element and then propagated to its parent elements.

Event capturing, on the other hand, is the opposite of event bubbling. In event capturing, the event is first captured by the outermost element and then propagated to its nested elements.

To specify event capturing, you can use the addEventListener method with the third parameter set to true.

element.addEventListener(eventType, eventHandler, true);

Follow-up 4

In what scenarios would you use event bubbling?

Event bubbling can be useful in scenarios where you want to handle an event on multiple elements without attaching event listeners to each individual element. By attaching a single event listener to a parent element, you can handle events triggered by its child elements as well. This can help reduce code duplication and make event handling more efficient.

For example, you can use event bubbling to handle click events on a list of items where each item is a child element of a parent container. Instead of attaching a click event listener to each item, you can attach a single event listener to the parent container and use event bubbling to handle the click events on the individual items.

2. What is event delegation in JavaScript?

Event delegation is a pattern where you attach a single listener to a stable parent element instead of one per child, and let bubbling carry child events up to it. Inside the handler you identify the real source with event.target — typically event.target.closest(selector) to match the element (or an ancestor of it) you care about.

list.addEventListener('click', (event) =&gt; {
  const item = event.target.closest('li');
  if (!item || !list.contains(item)) return;
  console.log('clicked item', item.dataset.id);
});

Why interviewers like it:

  • Fewer listeners → less memory, simpler setup.
  • Works for dynamically added/removed children automatically — no need to re-bind when the DOM changes.

Gotchas: use closest() (not just event.target) so clicks on nested elements still match; guard against events from outside your intended set; and remember some events (focus/blur) don't bubble, so delegate with focusin/focusout or the capture phase instead.

↑ Back to top

Follow-up 1

Can you provide an example of event delegation?

Sure! Here's an example of event delegation in JavaScript:

// HTML
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>

// JavaScript
document.getElementById('parent').addEventListener('click', function(event) {
  if (event.target.tagName === 'LI') {
    console.log('Clicked on item:', event.target.textContent);
  }
});

In this example, we attach a click event listener to the parent <ul> element. When a <li> element is clicked, the event bubbles up to the parent element and the event listener checks if the clicked element is an </li> <li> element. If it is, it logs the text content of the clicked element.

Follow-up 2

What are the benefits of using event delegation?

There are several benefits of using event delegation in JavaScript:

  1. Improved performance: By attaching a single event listener to a parent element instead of multiple event listeners to individual elements, you can reduce the number of event listeners in your code. This can lead to improved performance, especially when dealing with a large number of elements.

  2. Dynamic element handling: Event delegation allows you to handle events on dynamically added or removed elements without having to attach and remove event listeners on each element individually. This can be useful when working with dynamically generated content or when elements are frequently added or removed from the DOM.

  3. Simpler code: Event delegation can result in cleaner and more maintainable code. Instead of scattering event listeners throughout your codebase, you can centralize event handling logic in a single location.

  4. Reduced memory usage: Since event delegation reduces the number of event listeners, it can help reduce memory usage in your application.

Follow-up 3

How does event delegation work with event bubbling?

Event delegation relies on the concept of event bubbling in JavaScript. When an event is triggered on an element, it first executes the event handlers attached to that element, then it bubbles up to its parent elements and executes their event handlers. This process continues until the event reaches the root element of the document.

With event delegation, you attach a single event listener to a parent element. When an event occurs on a child element, it bubbles up to the parent element and triggers the event listener attached to it. By checking the event.target property, you can determine which child element triggered the event and perform the desired action.

Event delegation takes advantage of event bubbling to handle events on multiple elements with a single event listener.

Follow-up 4

In what scenarios would you use event delegation?

Event delegation is particularly useful in the following scenarios:

  1. Dynamic content: When working with dynamically generated content, event delegation allows you to handle events on elements that may not exist when the page initially loads. This is especially useful when elements are added or removed from the DOM dynamically.

  2. Performance optimization: If you have a large number of elements that require event handling, attaching individual event listeners to each element can impact performance. Event delegation allows you to reduce the number of event listeners and improve performance.

  3. Efficient event management: Event delegation can simplify event management by centralizing event handling logic in a single location. This can make your codebase more maintainable and easier to understand.

  4. Event handling for nested elements: When you have nested elements with similar event handling requirements, event delegation can simplify the process by handling events on a parent element instead of attaching event listeners to each individual nested element.

3. How does JavaScript handle events on elements that are added dynamically?

You handle them with event delegation: attach one listener to a stable ancestor that exists at setup time, and rely on bubbling to catch events from children added later. Because the listener lives on the parent, it doesn't matter whether the child existed when you bound it.

const list = document.querySelector('#todo-list');

list.addEventListener('click', (event) =&gt; {
  const btn = event.target.closest('.delete');
  if (!btn) return;
  btn.closest('li').remove();
});

// Later: this dynamically added item is handled with no extra wiring
list.insertAdjacentHTML('beforeend',
  '<li>New task x</li>');

Use event.target.closest(selector) to match the element (or its ancestor) you care about, and bail early if it doesn't match.

The alternative — re-attaching listeners every time you insert an element — is error-prone and leaks listeners if you forget to remove old ones. Delegation avoids both. For non-bubbling events (focus/blur) delegate via focusin/focusout.

↑ Back to top

Follow-up 1

Can you provide an example of handling events on dynamically added elements?

Sure! Here's an example:

// HTML
<div></div>

// JavaScript
const parent = document.getElementById('parent');

parent.addEventListener('click', function(event) {
  if (event.target.matches('.child')) {
    console.log('Clicked on dynamically added element');
  }
});

// Adding a dynamically added element
const child = document.createElement('div');
child.classList.add('child');
parent.appendChild(child);

In this example, we attach a click event listener to the parent element. When a click event occurs, we check if the event target matches the selector '.child'. If it does, we log a message to the console.

Follow-up 2

How does event delegation help in this scenario?

Event delegation helps in handling events on dynamically added elements by allowing you to attach a single event listener to a parent element instead of attaching individual event listeners to each dynamically added element. This has several benefits:

  1. Improved performance: Attaching a single event listener to a parent element is more efficient than attaching multiple event listeners to each dynamically added element.
  2. Simplified code: With event delegation, you don't need to worry about attaching and removing event listeners for dynamically added elements. The parent element handles all the events.
  3. Dynamic element handling: Event delegation allows you to handle events on elements that are added or removed dynamically from the DOM.

Follow-up 3

What are the limitations of handling events on dynamically added elements?

While event delegation is a powerful technique for handling events on dynamically added elements, it does have some limitations:

  1. Event propagation: Event delegation relies on event propagation, which means that if an event is stopped from propagating (e.g., using event.stopPropagation()), the event will not reach the parent element and the event handler will not be executed.
  2. Performance impact: If the parent element has a large number of child elements or if the event handler logic is complex, event delegation can have a performance impact. In such cases, it may be more efficient to attach individual event listeners to the dynamically added elements.
  3. Event target limitations: Event delegation requires you to check if the event target matches a specific selector. This means that you need to know the structure and selectors of the dynamically added elements in advance.

4. What is the order of event propagation in JavaScript?

An event propagates through the DOM in three phases, in this order:

  1. Capturing phase — the event travels down from window/document to the target's parent. Handlers registered with { capture: true } fire here.
  2. Target phase — the event reaches the element it occurred on; that element's handlers run.
  3. Bubbling phase — the event travels back up from the target to the root. Default handlers (no capture option) fire here.
// Fires during capture (top → down)
parent.addEventListener('click', onCapture, { capture: true });
// Fires during bubble (target → up)
parent.addEventListener('click', onBubble);

Gotchas interviewers probe:

  • Overall order is capture (top→target) then bubble (target→top); on the target element itself, handlers run in registration order.
  • event.stopPropagation() halts the remaining journey; stopImmediatePropagation() also blocks other handlers on the same element.
  • Not all events bubble (e.g. focus/blur) — but they still capture, so capture-phase delegation works for them.
↑ Back to top

Follow-up 1

Can you provide an example demonstrating the order of event propagation?

Sure! Here's an example:




  Event Propagation Example


  <div>
    <div>
      Click me
    </div>
  </div>


    function handleClick(event) {
      console.log(event.currentTarget.id);
    }

    var outer = document.getElementById('outer');
    var inner = document.getElementById('inner');
    var button = document.getElementById('button');

    outer.addEventListener('click', handleClick, true);
    inner.addEventListener('click', handleClick, true);
    button.addEventListener('click', handleClick, true);



In this example, when you click the button, the event will propagate in the capturing phase from the outermost element (<div>) to the target element (`). The console will log the IDs of the elements in the order:outer,inner,button`.

Follow-up 2

How can we change the order of event propagation?

In JavaScript, we can change the order of event propagation by using the addEventListener method with the capture parameter set to true or false. When capture is set to true, the event will propagate in the capturing phase, and when capture is set to false (default), the event will propagate in the bubbling phase.

Follow-up 3

What is the difference between event propagation and event bubbling?

Event propagation and event bubbling are related concepts in JavaScript, but they refer to different phases of the event flow.

Event propagation refers to the process of an event being dispatched and propagated through the DOM tree. It consists of three phases: capturing phase, target phase, and bubbling phase.

Event bubbling, on the other hand, is a specific type of event propagation where the event starts from the target element and propagates up the DOM tree to the outermost element. It is the default behavior in JavaScript when the addEventListener method is used without specifying the capture parameter or setting it to false.

5. What is the purpose of the 'event.target' property in JavaScript?

event.target is the element that originally triggered the event — the deepest element where it occurred. It stays the same throughout propagation, no matter which ancestor's handler is currently running.

This is the key to event delegation: a parent's handler reads event.target to find out which child was actually interacted with.

list.addEventListener('click', (event) =&gt; {
  console.log(event.target);        // the exact element clicked
  console.log(event.currentTarget); // always `list` (where the handler lives)
});

The distinction interviewers want — target vs currentTarget:

  • event.target = what fired the event (can be a deeply nested child).
  • event.currentTarget = the element the running handler is attached to.

They differ during bubbling/delegation. Since target can be a nested descendant, pair it with closest() to reliably match the element you mean:

const item = event.target.closest('.item');
if (item) { /* handle */ }
↑ Back to top

Follow-up 1

Can you provide an example of using the 'event.target' property?

Sure! Here's an example:

Click me

Follow-up 2

How does 'event.target' work in the context of event delegation?

In the context of event delegation, 'event.target' is used to determine the most specific element that originally triggered the event, even if the event was actually triggered on one of its descendants. This is useful when you have multiple elements with the same event listener, and you want to perform different actions based on the specific element that was interacted with.

Follow-up 3

What is the difference between 'event.target' and 'event.currentTarget'?

The 'event.target' property refers to the element on which the event originally occurred, while the 'event.currentTarget' property refers to the element that currently has the event listener attached to it. In other words, 'event.target' represents the specific element that triggered the event, whereas 'event.currentTarget' represents the element that is currently handling the event.

Live mock interview

Mock interview: JavaScript Event Bubbling and Delegation

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.