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 item
s:
$('#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.
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
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 .trigger
is done just in case jQuery caches delegated events, which I believe it does. This would impact the test).
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
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
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.
That's the matter of optimization. Let me explain:
$('#items2 .item a').on('click', function(e) {
console.log("### Items2 click");
});
With the above code, each and every anchor have own event handler. It's waste of memory because just a single handler can do same action.
$('#items').on('click', '.item a', function(e) {
console.log("### Items click");
});
Also with the second code, if you append more anchors to #items
after binding, you don't need to add new event handler. The parent element, #items
, cover them already.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With