This is the second post in a series on bubbling, delegation and how to delegate events with jQuery. It assumes you’ve already read the first post What does event bubbling mean, or already have a grasp on event bubbling in JavaScript.
Event Delegation
When providing examples, I’m going to refer to the HTML we used as an example in the first post (shown below):
<div>
<h1>
<a href="#">
<span>Hello</span>
</a>
</h1>
</div>
Also note that I’m still not interested in adding support to older versions of IE (<9). You’ll have to add the normal fallback to attachEvent
instead of addEventListener
if you want to support them, and find an alternative for querySelector
/ querySelectorAll
. The event target
property exists as srcTarget
in older versions of IE.
So now we understand event bubbling… but how does it help us?
It means if we want to add an event handler for a click
on the <span>
element in the above example, we don’t need to add it to the <span>
element; we can add it to any of it’s ancestors… as shown here;
window.addEventListener('load', function () {
document.querySelector('div').addEventListener('click', function (e) {
if (e.target.nodeName === "SPAN") {
alert('Click event fired on the SPAN element');
}
}, false);
});
But yes, I hear you ask me again… how does this help us?
Imagine you have a table with hundreds of rows. Each row contains a <a />
to which you want to attach a click
handler to. With no event-bubbling you’d have to bind the event handler to each <a />
; which involves iterating over each element and adding an event handler individually to each one. See it in action here. Does it feel efficient to you?;
window.addEventListener('load', function () {
// Add loads of rows
var rows = '';
for (var i = 0; i < 100; i++) {
rows += '<tr><td>' + i + '</td><td><a href="#">Click Here</a></td></tr>';
}
document.querySelector("table").innerHTML = rows;
// End setup
// Attach event to each element
var elements = document.querySelectorAll('#the-table a');
for (var i=0;i<elements.length;i++) {
elements[i].addEventListener('click', function (e) {
alert('You clicked row #' + this.parentNode.previousSibling.innerText);
}, false);
};
alert('Event handler bound to ' + elements.length + ' elements');
});
Instead, what we could do is bind one click
handler to the <table />
.
document.querySelector('#the-table').addEventListener('click', function (e) {
if (e.target.nodeName === "A") {
alert('You clicked row #' + e.target.parentNode.previousSibling.innerText);
}
});
See this in action here.
Secondly, imagine you load some content dynamically and you want some capture some events on it. You can only add event handlers to elements once they exist (makes sense right?), so without event-bubbling you’d have to re-bind the same event handlers to your content each time you add the content.You can see this in an example here, where we add rows to the table programmatically when you click a button.
However, because the <table />
element does exist in the DOM when the page is rendered, we can add an event handler to this no problem, as shown here. This is a common problem when loading elements via AJAX as well; and attaching the event handler to an element that is in the DOM from the page load is the solution.
So in summary, you should be taking advantage of event bubbling by using event delegation if you want to handle an event for multiple elements, or you want to bind events to dynamically loaded data.
Now move onto the next article; Event Delegation with jQuery