In an effort to better understand jQuery performance, I’ve come across the following question. Consider the two approximately equal solutions for binding a click event to items in a list:
List items:
<div id="items">
<div class="item"><a href="#">One</a></div>
<div class="item"><a href="#">Two</a></div>
<div class="item"><a href="#">Three</a></div>
</div>
<div id="items2">
<div class="item"><a href="#">One</a></div>
<div class="item"><a href="#">Two</a></div>
<div class="item"><a href="#">Three</a></div>
</div>
Notice there are two idential lists (aside from the ID). Now, consider the following jQuery to bind the client events for each of the anchors in the items:
$('#items').on('click', '.item a', function(e) {
console.log("### Items click");
});
$('#items2 .item a').on('click', function(e) {
console.log("### Items2 click");
});
This achieves the same result in that clicking on items in the lists will output their respective message.
Observing the events that are being bound, in the first case, a click event is being bound to the #items container, with no events being bound to the children. However, in the second case, no click event is being bound to the parent #items2, but each of the children elements has a click event.
Now, my question is: is one clearly preferable over the other? Being naive, I would assume the first case is preferable, but lacking knowledge of the internals of jQuery, it may very well be likely that the two are equivalent under-the-hood.
I’ve prepared a fiddle to demonstrate the two cases. Observing the events that jQuery has built for the elements, is where I derived the above assumptions (you can see output in your web browser’s console).
Unless you are dealing with some huge number of elements, it probably does not matter either way. The question is when do you need to be more efficient: at bind time or at trigger time?
Disclaimer: I don’t claim that any of these tests are perfect. Also, I only tested in Chrome.
Binding time
I didn’t know the answer offhand, so I decided to just try and test everything out. First, I assumed that using delegation would be a lot faster for binding (it only has to bind once as opposed to X number of times). This appears to be correct.
http://jsperf.com/on-delegate-vs-not-bind-only
Do not delegate: 100% slower
Trigger time
Next, I figured that not using delegation may actually be faster for triggering the events because no DOM movement is needed to check event triggering. For whatever reason, I was incorrect about that:
http://jsperf.com/on-delegate-vs-not-trigger-pls
Do not delegate: 60% slower
(The initial
.triggeris done just in case jQuery caches delegated events, which I believe it does. This would impact the test).Triggering only one item
Then, I figured that not using delegation would be faster for triggering the event on one specific item. This too was wrong:
http://jsperf.com/on-delegate-vs-not-trigger-one
Do not delegate: 80% slower
This is so even if it’s not the last sibling, but one of the earlier siblings:
http://jsperf.com/on-delegate-vs-not-trigger-one-eq2
Deep Nesting
Finally, I figured that a lot of the work done by delegation has to be DOM tree parsing. That means that using delegation in a deeply nested DOM and binding to a very old ancestor and triggering takes longer than binding to and triggering on the deeply nested item itself.
This finally turned out to be correct:
http://jsperf.com/on-delegate-vs-not-deep-nesting
Delegate: 90% slower
Conclusion
I can’t draw any monumental conclusions here, but if you have a ton of DOM to work with, especially if it’s deeply nested, you might take a look at using delegation for binding rather than binding directly.
If anything, these examples have taught me (well, reinforced anyway) that you should try to keep the delegating element as close to the descendants that will trigger the event as possible.