Event Delegation in JavaScript
You have a list of 200 items, each needing a click handler. The obvious move is to loop over them and attach a listener to every one. It works, until the list grows, until items get added on the fly, until you're babysitting 200 listeners and the page feels sluggish.
There's a better way: attach one listener to the parent and let it handle clicks for every child. That's event delegation, and it rides on a built-in DOM behavior called bubbling.
First, Bubbling
Click an element and the event doesn't stop there. It travels up through every ancestor, from the thing you clicked to its parent, on up to document. That's bubbling.
Click "About" and the event fires on the <li>, then bubbles to the <ul>, then <body>, then document. A listener on any of them hears the click. Delegation puts the listener on the ancestor and lets it catch everything below.
The Naive Way
One listener per item:
200 items means 200 listeners in memory. Add a new <li> later and it's dead, no listener attached. Remove items and you've got cleanup to worry about. It scales badly in every direction.
The Delegated Way
One listener on the parent. When a click bubbles up, check event.target to see which child was actually clicked.
One listener now handles every item, even ones added later. Two pieces make it work:
event.targetis the deepest element actually clicked, not the one holding the listener (that'sevent.currentTarget).closest("li")walks up to the nearest matching ancestor. So if your<li>has an icon inside it, a click on the icon still resolves to the right item.
event.target is what the user clicked. event.currentTarget is the element whose listener is running (the parent). Delegation lives in the gap between the two.
Why It's Better
The listener count stays at one whether you have 5 items or 5,000. New children just work, because the parent was always there. There's nothing to clean up when items disappear. Less code, less memory, fewer bugs.
A Few Gotchas
Delegation isn't magic, watch for these:
- Some events don't bubble.
focus,blur,mouseenter, andmouseleavewon't reach the parent. Usefocusin/focusout(which do bubble) instead. stopPropagation()in a child kills the event before it reaches your delegate. Use it deliberately.- Don't delegate everything to
document. Pick the closest stable parent that wraps the elements you care about.
The Takeaway
Bubbling means a child's click passes through every ancestor, so one listener on the parent can do the work of hundreds. Read event.target (plus closest()) to find what was clicked, and you get less code, less memory, and dynamic content for free.
So next time you're about to loop and attach the same handler to every element, stop. One listener on the parent. Let bubbling do the rest.