is there any difference between this both selectors in combination with a click event?
$("#container").find(".element").on("click",function(){
})
$("#container").on("click", ".element",function(){
})
For me I think technically the effect and consequence will be the same?
Thank you
They are not the same.
The first example using find().on() looks for the .element class in the DOM and adds the event handler to it. It will not work for any elements with that class that are added to the DOM later in the page lifecycle.
The second example using on() with a selector is a delegated event handler, and will therefore work for all matching elements in the DOM as well as those added later.
Related
I am trying to understand this particular difference between the direct and delegated event handlers using the jQuery .on() method. Specifically, the last sentence in this paragraph:
When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
What does it mean by "runs the handler for any elements"? I made a test page to experiment with the concept. But both following constructs lead to the same behavior:
$("div#target span.green").on("click", function() {
alert($(this).attr("class") + " is clicked");
});
or,
$("div#target").on("click", "span.green", function() {
alert($(this).attr("class") + " is clicked");
});
Maybe someone could refer to a different example to clarify this point? Thanks.
Case 1 (direct):
$("div#target span.green").on("click", function() {...});
== Hey! I want every span.green inside div#target to listen up: when you get clicked on, do X.
Case 2 (delegated):
$("div#target").on("click", "span.green", function() {...});
== Hey, div#target! When any of your child elements which are "span.green" get clicked, do X with them.
In other words...
In case 1, each of those spans has been individually given instructions. If new spans get created, they won't have heard the instruction and won't respond to clicks. Each span is directly responsible for its own events.
In case 2, only the container has been given the instruction; it is responsible for noticing clicks on behalf of its child elements. The work of catching events has been delegated. This also means that the instruction will be carried out for child elements that are created in future.
The first way, $("div#target span.green").on(), binds a click handler directly to the span(s) that match the selector at the moment that code is executed. This means if other spans are added later (or have their class changed to match) they have missed out and will not have a click handler. It also means if you later remove the "green" class from one of the spans its click handler will continue to run - jQuery doesn't keep track of how the handler was assigned and check to see if the selector still matches.
The second way, $("div#target").on(), binds a click handler to the div(s) that match (again, this is against those that match at that moment), but when a click occurs somewhere in the div the handler function will only be run if the click occurred not just in the div but in a child element matching the selector in the second parameter to .on(), "span.green". Done this way it doesn't matter when those child spans were created, clicking upon them will still run the handler.
So for a page that isn't dynamically adding or changing its contents you won't notice a difference between the two methods. If you are dynamically adding extra child elements the second syntax means you don't have to worry about assigning click handlers to them because you've already done it once on the parent.
The explanation of N3dst4 is perfect. Based on this, we can assume that all child elements are inside body, therefore we need use only this:
$('body').on('click', '.element', function(){
alert('It works!')
});
It works with direct or delegate event.
Tangential to the OP, but the concept that helped me unravel confusion with this feature is that the bound elements must be parents of the selected elements.
Bound refers to what is left of the .on.
Selected refers to the 2nd argument of .on().
Delegation does not work like .find(), selecting a subset of the bound elements. The selector only applies to strict child elements.
$("span.green").on("click", ...
is very different from
$("span").on("click", ".green", ...
In particular, to gain the advantages #N3dst4 hints at with "elements that are created in future" the bound element must be a permanent parent. Then the selected children can come and go.
EDIT
Checklist of why delegated .on doesn't work
Tricky reasons why $('.bound').on('event', '.selected', some_function) may not work:
Bound element is not permanent. It was created after calling .on()
Selected element is not a proper child of a bound element. It's the same element.
Selected element prevented bubbling of an event to the bound element by calling .stopPropagation().
(Omitting less tricky reasons, such as a misspelled selector.)
I wro te a post with a comparison of direct events and delegated. I compare pure js but it has the same meaning for jquery which only encapsulate it.
Conclusion is that delegated event handling is for dynamic DOM structure where binded elements can be created while user interact with page ( no need again bindings ), and direct event handling is for static DOM elements, when we know that structure will not change.
For more information and full comparison -
http://maciejsikora.com/standard-events-vs-event-delegation/
Using always delegated handlers, which I see is current very trendy is not right way, many programmers use it because "it should be used", but truth is that direct event handlers are better for some situation and the choice which method use should be supported by knowledge of differences.
Case 3 (delegated):
$("div#target").delegate("span.green", "click", function() {...});
I always wondered which is the better way of handling events in terms of code manageability, cleanliness and code reuse.
If you use the former then say a list of 10 anchor tags with click handler will have something like:
Click Me
Click Me
Click Me
... 10 times
which looks kind of odd.
With the latter method, using anonymous function, it'd be like:
$('a').on('click', function(e){});
At the end of the day, every event is bound to some element in the DOM. In the case of .bind, you're binding directly to the element (or elements) in your jQuery object. If, for example, your jQuery object contained 100 elements, you'd be binding 100 event listeners.
In the case of .live, .delegate, and .on, a single event listener is bound, generally on one of the topmost nodes in the DOM tree: document, document.documentElement (the element), or document.body.
Because DOM events bubble up through the tree, an event handler attached to the body element can actually receive click events originating from any element on the page. So, rather than binding 100 events you could bind just one.
For a small number of elements (fewer than five, say), binding the event handlers directly is likely to be faster (although performance is unlikely to be an issue). For a larger number of elements, always use .on.
The other advantage of .on is that if you add elements to the DOM you don't need to worry about binding event handlers to these new elements. Take, for example, an HTML list:
<ul id="todo">
<li>buy milk</li>
<li>arrange haircut</li>
<li>pay credit card bill</li>
</ul>
Next, some jQuery:
// Remove the todo item when clicked.
$('#todo').children().click(function () {
$(this).remove()
})
Now, what if we add a todo?
$('#todo').append('<li>answer all the questions on SO</li>')
Clicking this todo item will not remove it from the list, since it doesn't have any event handlers bound. If instead we'd used .on, the new item would work without any extra effort on our part. Here's how the .on version would look:
$('#todo').on('click', 'li', function (event) {
$(event.target).remove()
})
Second method is preferrable, since we should not be mixing our JavaScript with the HTML. (Separation of Concerns) . This way your code is kept clean.
This also works well with dynamically inserted HTML code.
`$('a').on('click', function(e){});` // Using jQuery.
Using Vanilla JS:
document.getElementById("idName").addEventListener("click", function(){}); // Bind events to specific element.
document.addEventListener("click", function(){}); // Bind events to the document. Take care to handle event bubbling all the way upto the document level.
This part of code is an answer from this question.
$(document.body).on('change', 'select[name^="income_type_"]', function() {
alert($(this).val());
});
I Have two questions the first one is.
Is there a performance issue selecting 'document.body' instead of selecting the Parent element of select?
Something like this.
Second question is.
It will be function like '$.live()' when putting Parent element instead of document.body?
$("#IdOfParentHere").on('change', 'select[name^="income_type_"]', function() {
alert($(this).val());
});
Thanks!
There could be a performance impact(not much significant though) when attaching event handlers to body instead parent element.
Take a case where you are trying to delegate a click event, what you really want to is to handle dynamically created li elements which are in a static ul element. In event delegation when an event happens inside the attached element that events target will be evaluated against the delegation selector to see whether to trigger the handler. In this case if the event is attached to the ul only events inside the ul has to be tested, but if the hanlder is attached to body all the click in the page will have to be tested.
The live() method attaches the handler to the document object, so yes it will be similar to that.
This method provides a means to attach delegated event handlers to the
document element of a page, which simplifies the use of event handlers
when content is dynamically added to a page.
Is there a performance issue selecting 'document.body' instead of selecting the Parent element of select?
No. There is no significant difference in performance. Of course it would be a little faster if you put it closer on the DOM, but we are talking about an incalculably small difference.
Second question is. It will be function like '$.live()' when putting Parent element instead of document.body?
$.live does exactly the same thing as $("body").on("click", "selector",
I've a table generated dynamically by jQuery, using
this.html("<table><tr><td><div>Click Me</div></td></tr></table>");
within the table, I've a few divs (my sample shows only one to keep things simple), which I want to add click event handler to. I'd like to keep html clean and use as much of jQuery power as I can, but since I'm doing an 'eval' type of things I can't quite figure out how to do that.
I know, that I can use $("div[some attribute selector]").on("click", {}, clickHandler);, but is it a good idea in my case?
You need delegated events. To do that, simply use jQuerys on() method like this:
$(document.body).on('click', 'div', function( event ) {
// do something
});
Ref.: .delegate(), .on()
What is that? Almost all events do what we call 'bubble'. That means, if you click on a nested element, your browser looks if there is any click-event handler ascociated on that node. If so, it executes them and then the parent of that element is also asked if there is any click-event handler. This continues until either some handler prevents the event from further bubbling or we have reached the document.documentElement (html).
So, you should change the above document.body into the closest node relative to your dynamically added elements.
You can use either use live or delegate to do that
I'm creating and removing HTML from inside a div with jQuery (shopping cart, adding/removing items). I need to access the .click() event of a link inside this div, but it only works when the page first loads - after that link is removed then re-added it doesn't respond to any jQuery events.
I've tried functions both inside and outside of $j(document).ready(function() {}. How can I make events work on this link again after re-creation?
Use .delegate() instead of .click() (which is short-hand for .bind('click')):
$(<root-element>).delegate('a', function () {...});
Attach a handler to one or more events for all elements that match the
selector, now or in the future, based on a specific set of root
elements.
Source: http://api.jquery.com/delegate/
The <root-element> can be the document element or if you know an element that is always present that is a descendant of the document element it is best to use that element.
You need to either reattach the event every time you overwrite the content of your container div or set handler using live/delegate/on depending on the version of jQuery you use.
Second method is in general more elegant, but has drawbacks. In particular you cannot cancel the default action from cascaded even attached to the container.
The .click() event only works for elements that are present with the function is called. You need to look into using either .live() or .delegate() to attach listeners to elements that are dynamically created after $(document).ready()
Try using .detach() instead of .remove(), that will keep the events. Alternatively use event delegation.