Event delegation is a technique where a single event listener on a parent element handles events for multiple child elements by using event bubbling and checking the event target. Edge cases include stopPropagation and nested targets; test performance in large lists.
Explain Event Delegation in JavaScript
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
The Core Idea
Event delegation lets you attach one event listener to a common ancestor instead of multiple listeners to each child. When an event occurs, it bubbles up the DOM, and you check which child element triggered it using event.target.
// Example: Without delegation
const items = document.querySelectorAll('li');
items.forEach(item => item.addEventListener('click', () => {
console.log('Clicked:', item.textContent);
}));
// Example: With event delegation
const list = document.querySelector('ul');
list.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
console.log('Clicked:', event.target.textContent);
}
});
Concept | Explanation | Example / Note |
|---|---|---|
Event Bubbling | Events start from the target element and bubble up to its ancestors. |
|
event.target | The original element that triggered the event. | Used to identify which child was clicked |
event.currentTarget | The element that the event listener is attached to. | Usually the parent in delegation |
Why Use Event Delegation?
- Better performance — fewer event listeners in the DOM.
- Handles dynamic elements added later (since the listener is on a stable ancestor).
- Cleaner, more maintainable code for lists, tables, or repeated components.
// Example: Dynamic content
const container = document.querySelector('#buttons');
container.addEventListener('click', (e) => {
if (e.target.matches('button.delete')) {
e.target.remove();
}
});
// Works even if buttons are added later!
Common Pitfalls
- Forgetting to check
event.target→ all clicks bubble and trigger the handler. - Relying on
event.targetwhen nested elements are inside clickable items (use.closest()to handle this safely).
// Example: Using closest() for nested targets
list.addEventListener('click', (event) => {
const li = event.target.closest('li');
if (!li) return; // click outside any li
console.log('Clicked item:', li.textContent);
});
Practical scenario
A long, dynamic list of comments can be handled with a single click listener on the list container.
Common pitfalls
- Using
event.targetdirectly instead ofclosest()to find the intended item. - Forgetting that
stopPropagationcan block delegation. - Not handling keyboard events, so accessibility suffers.
Delegation saves memory but adds selector logic. Test by adding/removing items dynamically and ensuring handlers still work.
Imagine a restaurant. Instead of each waiter (child) taking orders, there’s one manager (parent) who hears all requests and decides who called — that’s event delegation!
- Attach listener on parent, not each child.
- Use event bubbling +
event.target(orclosest()) to detect the real source. - Improves performance and works for dynamically added elements.