In JavaScript, event delegation is a concept of listening to specific events on some element (eg. the whole document) to catch these events being fired on any of the child elements, even the ones that did not exist when the event was being attached. This happens thanks to another concept called "event bubbling", which makes events to be visible by parent elements. Within every event handler it is possible to check what was the target element of the event.
You might use this tag if your question sounds like one of these:
- I dynamically inserted/modified nodes in my document, and now they don't handle events.
- How can I bind event handlers for nodes that don't exist yet?
- I'm binding thousands of event handlers in my document, and now it runs super slow.
- I need to bind the same event handler to lots of different elements. Isn't there an easier way?
If so, then you might want to use event delegation. The advantages of delegation are:
- Use a single handler to capture many events on many different nodes.
- Support dynamic page changes without having to constantly re-bind event handlers.
To understand delegation, you first have to be familiar with event bubbling.
Bubbling
Many of the most common events in JavaScript "bubble" up the DOM tree. For example, consider the following HTML:
<div id="imageDiv">
<img src="/abc.png" class="clickable"/>
<img src="/def.png" class="clickable"/>
<img src="/ghi.png" class="clickable"/>
<p>
<img src="/jkl.png" class="clickable"/>
</p>
</div>
If a user clicks on the jkl.png
image the click event will be fired first on the image itself, then on the containing paragraph, then on the div
element, and so on all the way up to the document object.
Read more about Event Bubbling
An excellent article on Bubbling vs Catching
Delegation
You can take advantage of event bubbling to implement a single handler for a large number of nodes.
Instead of binding four click event handlers (one for each image), you only need one handler attached to the div
element. The event object contains information about which element originally received the event (the target). You can test the target to find out if you should handle the event or not.
Here we bind a click handler to div#imageDiv
and use it to handle click events on the descendant images:
document.getElementById('imageDiv').onclick = function(evt) {
if (evt.target.className == 'clickable') {
// ... do something here ...
}
};
Now your single handler will catch all of the click events for all of the images inside div#imageDiv
. Also, you can add images to div#imageDiv
dynamically and not have to worry about binding new click handlers for them.
Of course there are browser compatibility issues with how some events bubble, and with finding the original target of the event. That is why a good JavaScript library will give you a convenient command for delegation. jQuery provides the on() method:
$('#imageDiv').on('click', 'img.clickable', function(evt) {
// ... "this" is the image that was clicked ...
});