jQuery Event Delegation
Recently, I've been working on adding some extra features to a shopping cart page using jQuery. The idea is that you select your country from a dropdown and an AJAX(Asynchronous JavaScript and XML) request is performed, which updates a list of radio buttons on the page. If you select one of these radio buttons, it triggers another AJAX(Asynchronous JavaScript and XML) request, which updates the postage prices for the given items in the basket.
Each of the radio buttons has an event like this bound to it.
1
2
3
$('#options input').click(function() {
// lookup postage prices via ajax
});
This triggers the AJAX(Asynchronous JavaScript and XML) request which looks up the postage prices from the database. It works fine with the default set of radio buttons that are present on the page when it loads. The problem comes when new radio buttons are added to the page via AJAX(Asynchronous JavaScript and XML). When a new radio button is added to the list, it doesn't automatically have a click event (like the one above) bound to it like the existing items do. The events are not automatically carried across to the new item you've added.
We can get around this problem by re-binding the events each time a new radio button is added to the page. But this can be messy and there is a much cleaner way of solving the problem.
Event delegation to the rescue
This is where event delegation comes in. Event delegation takes advantage of
the fact that browsers "bubble" events up the DOM(Document Object Model) when
they are triggered on a page. For example, given the HTML(HyperText Markup
Language) below, when clicking the radio button the browser will generate a
click event which will "bubble" upwards so that first the <input>
element
receives the event, followed by the <p>
, then the <div>
, and so on.
1
2
3
4
5
6
7
8
<div id="options">
<p>
<input type="radio" value="1" id="postage_method_1" />
</p>
<p>
<input type="radio" value="2" id="postage_method_2" />
</p>
</div>
We can take advantage of this "event bubbling". Instead of binding the event to
each individual input
, we'll bind it to a parent element instead and let the
click events from the inputs bubble up to the parent element. In this case, the
div called options.
1
2
3
4
5
$('#options').click(function(event) {
if ($(event.target).is('input')){
// do ajax request
});
});
Now, each time we add an additional input to the page, it will automatically respond to the same event as the rest of the inputs.
It's worth noting that this event will get triggered whenever any element under
the #options
div is clicked, so inside the event we check that the target of
the event was an input
element. This will restrict the code inside the event
so that it only executes when it's an input
that's been clicked.
Conclusion
Event delegation greatly improves the readability of the code. It sounds scary, but really isn't. It also has the added benefit of speeding up the processing of the page because you're not spending time looping through many DOM elements (which takes time) in order to add your event handlers.
Check out the references below for a much better explanation of how event delegation and binding of event handlers work.
References
- Event Delegation versus Event Handling
- Event Delegation with jQuery
- Event Delegation Made Easy
- Learning jQuery - Working with Events, part 1, and part 2