This question already has answers here:
Direct vs. Delegated - jQuery .on()
(6 answers)
Closed 9 years ago.
What is the difference Between the following statements. Which one should I choose over one another in a situation.
$(document).on("click","#btn",callback);
$("#btn").on("click",callback);
Have a look at the event delegation jQuery documentation
basically
$(document).on("click","#btn",callback);
will bind the click to the document DOM rather than the element directly which is useful when you are appending DOM elements to the window in which case the #btn selector will not exist yet.
Basically wiht direct
$(document).on("click","#btn",callback);
you're assigning events to document that affect matching descendants (#btn) which may or may not exist at the time that line of code is run.
$("#btn").on("click",callback);
With delegated you're affecting all matching elements that do exist at the time that line of code is run, and no matching ones that are created later.
http://api.jquery.com/on/
Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on(). To ensure the elements are present and can be selected, perform event binding inside a document ready handler for elements that are in the HTML markup on the page. If new HTML is being injected into the page, select the elements and attach event handlers after the new HTML is placed into the page. Or, use delegated events to attach an event handler, as described next.
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. By picking an element that is guaranteed to be present at the time the delegated event handler is attached, you can use delegated events to avoid the need to frequently attach and remove event handlers. This element could be the container element of a view in a Model-View-Controller design, for example, or document if the event handler wants to monitor all bubbling events in the document. The document element is available in the head of the document before loading any other HTML, so it is safe to attach events there without waiting for the document to be ready.
direct
$("#btn").on("click",callback);
Element with id present in DOM get clicked call callback. If it is
present in DOM handler will get attached.
binds the event listener directly to the elements
delegated
Read Event Delegation.
Use .on()
$(document).on("click","#btn",callback);
When a element in document is added with id btn than on click of that element run
callback function .
Binds the event listener to the document object
Syntax
$( elements ).on( events, selector, data, handler );
Related
This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 7 years ago.
I have a <select> form element that is being generated dynamically after the page loads. I was using jQuery to attach a change event that would update other fields in the form, and wanted to eliminate the need to rewire the handler each time the form containing the <select> was replaced.
For click events on dynamic elements, I have been moving to unobtrusive JavaScript and wanted to use that approach for change - but no success. Attaching to the <select>directly was cumbersome because when the node is replaced in the DOM, the connected event listener dies with it, and the new node must be attached to again.
So I wanted to know, is there a way to unobtrusively assign a change event handler to the parent element of a newly-created <select> and let the event bubble up to a handler on a previously-existing element, in order to avoid attaching a handler each time a new target input element is created?
Yes!
$('#StaticParentNode').on('change',"select.qualifiers", function(){
alert("Change detected from parent");
});
Naturally, I finally got this working 2 minutes after posting my original answer (below). Doh.
No.
While attaching unobtrusive event listeners on a parent <div> or the document works great for bubbling click events from elements that didn't previously exist on the page, you can only attach an event listener to an element that has that type of event.
As stated in the jQuery .change API docs (although this issue is not jQuery specific):
The change event is sent to an element when its value changes. This event is limited to <input> elements, <textarea> boxes and <select> elements.`
Since <div> and document do not have a change event, they cannot intercept one from their children either.
Solutions:
Just set onchange to a previously-defined function when you generate the element: <select onchange='MyModule.selectChanged()'>[<options>]</select>. Not pure from a separation of concerns standpoint, but a simple approach with minimal extra HTML added to an AJAX request, and no fiddling with handlers after the AJAX call completes.
or,
(Re)attach an event listener each time you create new elements that require it, either 'manually' (e.g. in a $.ajax success function) or by wiring it into whatever tool is handling your AJAX results (e.g. modifying or extending .prototype). See examples elsewhere on SO.
How to attach all events after manipulating the dom using ajax response. I have a ajax request which get a html response which is basically a fragment of html. that fragment HTML have many buttons. I want to refresh the dom so previously declared and attached events be applied into that fragment too. I dont want to keep on adding each events for each button using jquery on(). how else to do it?
You can use delegated event handling which is set up ahead of time and can be made to apply to newly added DOM elements. Delegated event handling is done with .on() and generally takes the form of:
$("static parent selector").on('click', 'selector for dynamic element', fn);
There is no clean way to just run your event installing code again and have it only apply to newly added DOM elements. You would have to put that code in a function and code it such that it never adds an event handler more than once and then you could call that function again after adding items to the DOM. Or, you could make the function take an argument for a parent object and only add event handlers in the newly added DOM hierarchy.
Here's another relevant answer about delegated event handling: Does jQuery.on() work for elements that are added after the event handler is created?
How to attach all events after manipulating the dom using ajax response. I have a ajax request which get a html response which is basically a fragment of html. that fragment HTML have many buttons. I want to refresh the dom so previously declared and attached events be applied into that fragment too. I dont want to keep on adding each events for each button using jquery on(). how else to do it?
You can use delegated event handling which is set up ahead of time and can be made to apply to newly added DOM elements. Delegated event handling is done with .on() and generally takes the form of:
$("static parent selector").on('click', 'selector for dynamic element', fn);
There is no clean way to just run your event installing code again and have it only apply to newly added DOM elements. You would have to put that code in a function and code it such that it never adds an event handler more than once and then you could call that function again after adding items to the DOM. Or, you could make the function take an argument for a parent object and only add event handlers in the newly added DOM hierarchy.
Here's another relevant answer about delegated event handling: Does jQuery.on() work for elements that are added after the event handler is created?
Yesterday I was reading the jQuery docs for .on() where was stated:
Avoid excessive use of document or document.body for delegated events on large documents
But today, I was looking at this JSPERF and I notice a better performance when the click event is attached to the document.
So right now, I'm confused. The performance tests speak against the docs?
Your JSPerf here is testing the speed to attach events, not the effect that they have on cumulative page performance. This is the wrong thing to test!
Javascript events propagate up the DOM all the way to the document root. This means that if you have an on("click", ...) handler on document, then every click on every element in the document will end up running an event handler, so jQuery can test if its origin matches the delegate target, to see if it should be passed to that event handler.
Imagine that your page has 10 different delegated event handlers on document, all handling various clicks. Every time you click any element in the page, the event will bubble up to the document root, and all 10 of those handlers have to be tested to figure out which (if any) should be run.
In general, you want your delegated events to be as deep in the tree as possible while still enabling your functionality, since this limits the number of elements that may invoke this event, and you can handle the event earlier to prevent it from propagating up the DOM tree.
It depends.
You can attach handler to any element you want, of course, and in some cases you will have to attach it to document or body (if you, for example, want to target all the links on the page). But, if you are sure that certain elements will always appear only inside given element (which is already created) - then for performance sake, you can attach event handler to that common parent.
The point is excessive.
IMHO excessive delegates on any DOM is terrible
The pagination controls on a page I am working on were being bound conditionally on there being more than 1 page. I don't like to see the following code in my projects,
if (pages > 1) {
$('.some_class').bind('event', function() {});
}
because I feel it represents a disorganized coding style. I would put it on the same level as sprinkling return statements here and there rather than using control. I feel like binding events to globally available objects has no place in the local scope of a function call. So what I usually do is make two javascript files, for example: pagination.js and pagination-controls.js. In the one I have logic about building the html and displaying the the pagination controls. In the other I have statements like the following:
$(document).on('click', '.pagination .next', function() {});
Which fires regardless of whether there is a $('.pagination .next') element anywhere on the page. I like the way that feels: the website has behaviours and it only knows about ids and classes, not about instance variables in some local scope somewhere.
EDIT: this is definitely bad practice, as mentioned below. However:
As of jQuery 1.7, the .on() method is the preferred method for
attaching event handlers to a document.
and the discussion on direct and delegated events is relevant. In particular I think the following describes my usage:
By picking an element that is guaranteed to be present at the time the
delegated event handler is attached, you can use delegated events to
avoid the need to frequently attach and remove event handlers. This
element could be the container element of a view in a
Model-View-Controller design, for example, or document if the event
handler wants to monitor all bubbling events in the document.
EDIT: So I guess now I'm wondering "is it bad to prefer binding behaviours to parent elements unconditionally over binding based on logic?" That's perhaps just a question of style, and my original question has been answered so I think I will accept the answer.
Yes, this is causing significant unnecessary overhead, and it is a "bad practice".
Binding your event handling to the top-level document object means that every single click that occurs on any element anywhere in your page will bubble up to the document object, where the event's target is checked to see if it matches .pagination .next.
In fact, the documentation itself recommends against your usage:
Attaching many delegated event handlers near the top of the document tree can degrade performance. Each time the event occurs, jQuery must compare all selectors of all attached events of that type to every element in the path from the event target up to the top of the document. For best performance, attach delegated events at a document location as close as possible to the target elements. Avoid excessive use of document or document.body for delegated events on large documents.
So, you're misusing on. It's for binding directly to elements or to parent elements which may have dynamically created children, and you are meant to bind to the closest possible parent element. Binding to the document is certainly not meant to be the only way you handle events in your page.