What is the different in performance and the handling of these two different jQuery statements:
Number One:
$('#selector1, #selector2, .class1').on('click', function () {
//stuff
});
Number Two:
$(document).on('click', '#selector1, #selector2, .class1', function () {
//stuff
});
I know that one does delegation and the other doesn't.
But what does that mean?
Don't both do some sort of action when you click on '#selector1, #selector2, .class1'
?
In the end, isn't it the same?
Number One will hook the click
event on the elements that exist when you execute that statement. E.g., the handler is directly hooked to the actual elements that match when you execute that call.
Number Two will hook the click
event on the document, and on receipt of any clicks, will check to see if the element that was actually clicked matches any of the given selectors, and if so will fire the handler. This is event delegation rather than a direct hookup.
So that means several things:
- Using Number Two, clicks on elements you add later will trigger the handler; with Number One they won't.
- Only bubbling events like
click
work with Number Two (because it relies on the event bubbling up the DOM to the document level).
- If you use a delegated handler (Number Two) and some other code hooks the event on the actual element and then cancels propagation (bubbling) of the event, the delegated handler won't see it.
- The delegated form (Number Two) has to match the element that was clicked (and potentially its ancestors) against the selector when the click occurs, which takes a non-zero amount of time. Not necessarily much time, but more than a direct handler (which doesn't have to do that) would. If you have a lot of delegated handlers on the same element (in this case, the document), you might start noticing.
There are times when using a directly-hooked handler is better, and times when event delegation (usually using something more focussed than the document
, though) is better. Usually, the dividing line between those is a judgement call, but for example if you want to respond to clicks on table rows, you're probably better off hooking the click
event on the table
element with a tr
selector, rather than attaching the click
event of every single table row, particularly if you update the table dynamically. Whereas if you have a unique button you know exists in the DOM when you're hooking up your handler, and you want to fire a specific function when that button (but not anything else) is clicked, a direct handler probably makes more sense.
Here's an example (live copy):
HTML:
<p>Click me</p>
JavaScript:
jQuery(function($) {
$('p').on('click', function() {
display("Directly-attached handler fired. Click this paragraph and note the difference in what happens compared to when you click the 'click me' paragraph.");
});
$(document).on('click', 'p', function() {
display("Delegated handler fired.");
});
function display(msg) {
$("<p>").html(msg).appendTo(document.body);
}
});
Note that when you click the "click me" paragraph, you get two new paragraphs added to the document, one of them the result of the first on
call, the other the result of the second. But note that if you click either of those two new paragraphs, you only see the handler from the second on
call (the delegated one), not the first. That's because those paragraphs didn't exist when you hooked up the first handler.
For anyone familiar with the pre 1.7 syntax, here's how on
can be used:
//bind syntax
$(selector).on(event, callback);
$(selector).bind(event, callback);
//delegate syntax
$(parent).on(event, selector, callback);
$(selector).delegate(selector, event, callback);
//live syntax
$(document).on(event, selector, callback);
$(selector).live(event, callback);
So your first line ($('#selector1, #selector2, .class1').on('click', function)
) is a bind
format to attach events to existing elements.
Your second line ($(document).on('click', '#selector1, #selector2, .class1', function)
) is a live
format to attach events to any element that matches the selector when the event occurs, whether or not the elements exist within the dom at the time of binding.
The first one binds 3+ event handlers (one to each element ) that are lost once the elements are replaced. The first one will fail if the elements don't exist yet.
The second one binds 1 event handler (one to document
) that will never be lost unless you explicitly remove it. And once the handler is called here, the event has already propagated to document
. Any time you click anywhere on the page, jQuery internally checks if it was on any element that matches the selector you gave and fires the handler you gave if it does. This is only if the event propagated to document
and wasn't stopped at lower level element.
The second one can be bound even before document ready, and it will still work even if the elements don't exist yet.