What happens in jQuery with .on() event if its element doesn't exist in the DOM?
For example, if I use this:
$(document).on('click', "#registerFormSubmit", function(){
// do something here
});
And I don't have #registerFormSubmit present on all pages, is the browser slowed down by the code, or is it not?
So why am I doing this anyway?
I don't want to split my javascript code to 10 .js files and include each depending on which is required on which page, as I believe the server/browser will transmit the data a lot faster if it's in 1 file (especially if the file is obfuscated and minified).
If the code slows down even pages not containing the element, would the following be a good solution to keep all the code in one file?
var page = window.location.pathname.split('/');
if (page[1] == 'contact'){
$(document).on('click', "#registerFormSubmit", function(){
// do something here
});
}
Remember that the .on() event attaches an event handler function for whatever element is in the DOM or will be in the DOM in the future. Therefore I believe it would slow the browser down even if the element isn't present at the moment.
However, the proposed if (page) solution should not attach the event if the page isn't matched, imo.
Can anyone shed some light on this, please?
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.
jQuery can process simple selectors of the form tag#id.class very quickly when they are used to filter delegated events. So, "#myForm", "a.external", and "button" are all fast selectors. Delegated events that use more complex selectors, particularly hierarchical ones, can be several times slower--although they are still fast enough for most applications. Hierarchical selectors can often be avoided simply by attaching the handler to a more appropriate point in the document. For example, instead of $("body").on("click", "#commentForm .addNew", addComment) use $("#commentForm").on("click", ".addNew", addComment).
source: http://api.jquery.com/on/
I will suggest you to use above mentioned format instead of $(document).on('click', "#registerFormSubmit") and if you are not going to use the click event in all pages then simple answer is don't put it in all .js files. Load only the required .js files and handle registerFormSubmit click event in separate .js file.
Related
Let's say I have bunch of click events. Also one/few of them is for document object.
Which one is better for performance? Click event for each element or :
document.addEventListener('click', (e)=>{
if(e.target == firstObject){ firstFunction(e) }
if(e.target == secondObject){ secondFunction(e) }
if(e.target == ThirdObject){ thirdFunction(e) }
})
Neither is "better." They each have their place in your toolkit.
A single delegated handler is more complex in that you have to do the kind of dispatch you're doing in your example (often using closest or matches), but has the advantage that if you're adding/removing elements you want to act on, you don't have to juggle event handlers.
Directly-assigned handlers are simpler (at least on elements that aren't added/removed), can prevent propagation, and let you keep your code more modular, more in keeping with the single responsibility principle.
Use the one that makes the most sense in a given context.
I think event listener for each element is better if possible, and makes sense in terms of code quality. There are some cases though where a document event listener will be needed ( for example to emulate a click outside behaviour)
That being said here are some of reasons that makes event listener for each element a better solution
event propagation is handled for you by the browser, if you decide to have only one event handler for the whole document, and u want to have event listeners for elements that are contained in each other, then you will need to handle propagation your self. That is to say you need to handle the order in which functions run yourself, and then you will have some either complex generic solution, or a specific imperative verbose code with a lot of if else statements.
Easier to read code, this is even more true for recent frameworks for web like react, angular, etc..., so for example assume you want to have a listener for clicks on the document, where that code should reside, in which file, and which component should own the code.
Removal of event listeners is handled for you by the browser apis, the browser gives you a way to remove event listeners. If you decide to go with a global event listener then you should handle removing event listeners yourself.
Your code will be hard to refactor and easier to break later, because you are coupling your document (or container ) event listener to your components internals. That is if you decide to change the structure of these components later, your document based event listener will probably break. This will depend a lot on how you identify the target of clicks, for example if you were identifying them by class names or other attributes, then these attributes might change later for reasons like styling.
and if you depend on ids for example you might eventually have unexpected results. because what happens for example if you added a listener for an element that has id, removed that element, and then later added another element with same id.
You miss on the development tooling provided for you by browsers, browsers can show you attached listeners for elements, with a document based event listener you wont be able to do that
It's better if you add one by one, because then you can remove event whenever it finish. Moreover you have more control about this event.
So something I'm curious about, how the YUI3 PJAX works. For instance, when used, even if you inject an anchor into the page with the yui3-pjax class and click it - that will run the AJAX function.
My question is does that use a Promise or what to determine if the anchor, including injected anchors, has the class?
I have a function for observing mutations for a site and I call it on the click event for the yui3-pjax anchors already existing in the page, but I also want to have it run on yui3-pjax anchors that I dynamically load into the page without having to recall the function.
Using jQuery for the ease of sample code, a similar solution can be written in vanilla Javascript as well.
You can use .on() with a selector parameter. For example:
$('body').on('click', '.class', function(e) {
e.stopPropagation(); //Stop multiple possible triggers from the same click
//TODO: Rest of code
});
The downside obviously being that every click on your highest common ancestor will get processed. The upside is however that since the click is caught there (not on the elements themselves) you don't have to worry about rebinding events.
I have lot of jquery scripts which dont handle elements loaded or created on the fly, of course I can convert all my scrits and add the them the .live() function. However was wondering if there is any option or trick that could automatically simulate the live function in all the scripts without modifying them one by one manually.
Thanks for the comments , live is depreciated, so I restate my question with the .on() function.
There is not one trick that will make all existing event handler code work with dynamically loaded elements without updating each event handler unless you want to replace some jQuery methods with methods that work differently than jQuery has documented (not recommended). You would have to replace all jQuery event handling methods that you are currently using with methods that forced delegated event handling into them. This would be a bad way to do this. Not only would you be hacking jQuery into something that would be different than it is documented and opening yourself up to compatibility issues with other code, but you'd be forced into the most inefficient use of delegated event handling (which is why .live() was removed in the first place). Do not do this. Fix your code to use the proper method of delegated event handling. It's not hard at all.
.live() has been deprecated and even removed from the latest versions of jQuery. You should not use it. There is a form of .on() that will allow you to use delegated event handling for dynamically loaded objects. You can see how to use the proper form of .on() for dynamically loaded elements in this post: jQuery .live() vs .on() method for adding a click event after loading dynamic html.
The "proper" way to use .on() for dynamic elements is like this:
$('#parent').on("click", "#child", function() {});
where you select the closest parent to the dynamic element that is not itself dynamically loaded and bind the event handler to that element.
.live() was removed because it put all delegated event handlers on the document object somewhat analogous to this:
$(document).on("click", "#child", function() {});
If, however, you used a number of delegated event handlers like this, performance could start to bog down. That's because when you do it this way and you click anywhere in the document and that click bubbles up to the document, it has to compare every single selector in every single .live() event handler you had to the current clicked object. Since selector comparisons are not always fast, this could really bog down the processing of events.
When you place the event handler on an object closer to the actual object, you end up with far event handlers there and thus far fewer selectors to compare to and processing of the events works faster.
Here's a reference on some differences between static event handlers and delegated event handlers and some useful notes on them: JQuery Event Handlers - What's the "Best" method
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.