Learn Event Delegation in JavaScript like you're 5
Attaching a single event listener to a parent element, instead of adding separate listeners to each child, is the technique known as event delegation. This works because of event bubbling, where an event (like a click) moves up the DOM tree from the child to the parent. By handling events at the parent level, you can save memory and improve performance, especially when dealing with a lot of child elements or elements that are added dynamically.
How Does It Work?
When an event is triggered on a child element (for example, a click), it doesn't stop there. The event bubbles up to its parent, and so on, up the DOM tree until it reaches the document root. Event delegation takes advantage of this by placing the event listener on a common ancestor of all the target child elements. This ancestor listens for events that bubble up from the child elements and handles them based on certain conditions, such as the type of event or the specific child element that triggered the event.
Example of Event Delegation
Let’s say we have a list of buttons, and we want to handle a click event for each button. Instead of adding a click event listener to each button, we can add a single listener to their parent element.
<ul id="buttonList">
<li><button data-action="delete">Delete</button></li>
<li><button data-action="edit">Edit</button></li>
<li><button data-action="view">View</button></li>
</ul>
Now, instead of adding a click
event listener to each button, we attach a single listener to the parent <ul>
element.
document.getElementById("buttonList").addEventListener("click", function(event) {
if (event.target.tagName === "BUTTON") {
const action = event.target.getAttribute("data-action");
if (action === "delete") {
console.log("Deleting item...");
} else if (action === "edit") {
console.log("Editing item...");
} else if (action === "view") {
console.log("Viewing item...");
}
}
});
The click
event listener is attached to the <ul>
element (the parent). Inside the event handler, we check if the clicked element is a button using event.target.tagName === "BUTTON"
. This ensures that we are only responding to button clicks. We use the data-action
attribute to determine the action to take (delete, edit, or view). This approach is more efficient and scalable, especially if we had many buttons or were dynamically adding new ones.
Another Example with Dynamic Elements
If the list of buttons was dynamically generated (e.g., adding new buttons via JavaScript), event delegation would still work perfectly without needing to reattach event listeners.
const ul = document.getElementById("buttonList");
// Dynamically adding new buttons
const newButton = document.createElement("li");
newButton.innerHTML = '<button data-action="share">Share</button>';
ul.appendChild(newButton);
// The same event listener on the parent will handle the new button
ul.addEventListener("click", function(event) {
if (event.target.tagName === "BUTTON") {
const action = event.target.getAttribute("data-action");
console.log(action + " button clicked.");
}
});
Event Bubbling: Event delegation works because of event bubbling, where an event triggered on a child element propagates up to its ancestors.
Efficient for Dynamic Content: Since we attach the event listener to the parent, it works for elements added later to the DOM.
Performance: Reduces the overhead of attaching and managing multiple event listeners, especially with a large number of child elements.
Event delegation is an essential technique for efficient DOM event handling, especially when dealing with many elements or dynamic content.
Subscribe to my newsletter
Read articles from Abeer Abdul Ahad directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Abeer Abdul Ahad
Abeer Abdul Ahad
I am a Full stack developer. Currently focusing on Next.js and Backend.