omg

idk really

hello there

3 min read
Cover image for idk really

Why Event Delegation Makes JavaScript Faster

If you've ever attached click handlers to hundreds of DOM elements, you've probably created more work for the browser than necessary.

A better approach is event delegation—a pattern where a single event listener handles events for many child elements.


The Problem

Imagine rendering a list with 1,000 buttons.

JavaScript
const buttons = document.querySelectorAll(".delete-btn"); buttons.forEach((button) => { button.addEventListener("click", () => { console.log("Delete item"); }); });

This creates 1,000 separate event listeners.

Although modern browsers are optimized for this, unnecessary listeners consume additional memory and increase maintenance complexity.


Event Delegation

Instead, attach a single listener to a parent element.

HTML
<ul id="todo-list"> <li> Buy milk <button class="delete-btn">Delete</button> </li> <li> Learn JavaScript <button class="delete-btn">Delete</button> </li> </ul>
JavaScript
const list = document.getElementById("todo-list"); list.addEventListener("click", (event) => { if (!event.target.matches(".delete-btn")) return; const item = event.target.closest("li"); item.remove(); });

Now only one event listener is required.


Why It Works

Events in the browser bubble.

When a button is clicked, the event travels upward through the DOM tree.

button
   ↑
li
   ↑
ul
   ↑
body
   ↑
document

Your parent element can inspect the original target and decide what to do.


Dynamic Content

Another advantage is that newly created elements automatically work without adding new listeners.

JavaScript
const li = document.createElement("li"); li.innerHTML = ` New Task <button class="delete-btn">Delete</button> `; document.getElementById("todo-list").appendChild(li);

No additional JavaScript is needed.


Handling Multiple Actions

You can even support multiple buttons with a single listener.

HTML
<button data-action="edit">Edit</button> <button data-action="delete">Delete</button> <button data-action="share">Share</button>
JavaScript
document.body.addEventListener("click", (event) => { const action = event.target.dataset.action; switch (action) { case "edit": console.log("Editing..."); break; case "delete": console.log("Deleting..."); break; case "share": console.log("Sharing..."); break; } });

TypeScript Version

TypeScript
const list = document.querySelector<HTMLUListElement>("#todo-list"); list?.addEventListener("click", (event: MouseEvent) => { const target = event.target as HTMLElement; if (!target.matches(".delete-btn")) return; target.closest("li")?.remove(); });

Performance Comparison

ApproachEvent ListenersDynamic ElementsMemory Usage
Individual Listeners1000❌ ManualHigher
Event Delegation1✅ AutomaticLower

Best Practices

  • Delegate from the closest stable parent.
  • Use event.target.matches() or closest() to identify elements.
  • Avoid delegating everything to document unless necessary.
  • Keep delegated handlers focused on one responsibility.

Common Mistakes

Forgetting closest()

JavaScript
// ❌ Might fail if an icon inside the button is clicked if (event.target.matches(".delete-btn")) { }

Instead:

JavaScript
// ✅ Works for nested elements const button = event.target.closest(".delete-btn"); if (button) { console.log("Delete clicked"); }

Delegating Too High

JavaScript
document.addEventListener("click", handleEverything);

While this works, it's often better to delegate from a closer container to reduce unnecessary event processing.


Conclusion

Event delegation is one of the simplest performance improvements you can make in JavaScript. It reduces memory usage, supports dynamically added elements, and keeps your codebase easier to maintain.

Whenever you're attaching the same event to many similar elements, consider whether a single delegated listener can do the job instead.