jQuery: better performance with check whether binding element exists? - javascript

I wonder... Let's imagine I have a code something like that:
$('#specific-element').change(some_long_and_ajax_function);
Element with binded ID doesn't exist on all of my pages. On some of them only. I do check whether this element exists like this:
if($('#specific-element').length > 0){
$('#specific-element').change(some_long_and_ajax_function);
// There can be more stuff related to this specific element
}
My question: is it worth it? Is there any performance impact for binding handlers for non-existing elements or checking length is worse than it? Or is it basically same and I have two useless rows? What would you recommend? The first one keeps code nice and clear but I'm not sure if this will be "healthy" for jQuery with dozens of examples like that. Thanks.

jQuery fails gracefully if the element doesn't exist, internally it does it's own check to see if the element exists, and if it doesn't the event handler isn't attached etc.
jQuery() calls jQuery.fn.init wich checks if the passed argument is a string, which in your case it is, it then calls jQuery.fn.find with the right context, and inside that it does
var i, ret = [],
self = this,
len = self.length;
... code
for (i = 0; i < len; i++) {
// add event handlers
}
so the loop never runs if there are no elements to run it on, so there's no need to do your own check, just let jQuery handle it.
EDIT:
When you call $('#specific-element').length you're already calling $(), and it does all the usual things internally and returns an array-like jQuery object that has a length property, and that's exactly what $('#specific-element').change... does as well, and jQuery's on() also returns rather quickly if no elements exists in the collection, so there really is no difference.
If you're really concerned about speed, you'd do something like
var el = document.getElementById('specific-element');
if ( el !== null ) {
el.addEventListener('change', fn, false);
}
but there's really no reason, just add event handler with jQuery the usual way, without checking it the element exists, it's what almost every website in existance does, and it works just fine.

As said in the comment, jQuery check if the element exist before binding events. But there is a speed difference wether you check or not.
When checking before binding, you save time if the element doesn't exist because getting a property (.length) is way faster than calling a function (which will probably call other functions) and failing gracefully.
But if the element exist, it will be slower since you add a condition before binding. Hence, it add 1 more step than if you did not check before and directly binded the event.
Just interpret those test results : http://jsperf.com/check-before-binding1
You can see that if the element exist, the difference between the check before is only 4000 operations / second more. It is not a lot... On the other hand, when you check if the element exist and it doesn't, it save 1,000,000 operations / second because it doesn't call the function .change().
Conclusion
I'd say checking before is better if that really matter, but mostly, it doesn't. If the element is most often present than missing on different pages, i'd directly bind the event. If the element is mostly missing than present, i'd check before binding.
In the end, we are talking about 0.0000001ms...
1I have slightly changed to code to optimise your. Caching the element sure is important is you want better performances.

Related

Should I add an 'if' statement to jQuery that doesn't execute on certain pages?

I like to keep my code clean as possible and I am just wondering what would be the best way to go around this problem when it comes to best practices.
I have the following function:
$('.car-hub-header-help, #assistance-overlay').click(function(){
$('#new-car-hub, #new-car-offer').toggleClass('assistance-active');
$('#pulman-assistance').toggleClass('pulman-assistance-active').css("top", fixedPositionCalculator);
$('#assistance-overlay').toggleClass('assistance-overlay-active');
$('#new-car').toggleClass('assistance-active-body');
$('#new-car-offer-cta').toggleClass('assistance-active-cta');
});
Now as you can see this function is very simple it just toggles classes based on a click event. One issue that I am having is that the element new-car-offer-cta is only on specific pages and it seems like this is bad practice to run that part of the function if the element isn't on some of my pages.
So I am just wondering if this would be better practice:
$('.car-hub-header-help, #assistance-overlay').click(function(){
$('#new-car-hub, #new-car-offer').toggleClass('assistance-active');
$('#pulman-assistance').toggleClass('pulman-assistance-active').css("top", fixedPositionCalculator);
$('#assistance-overlay').toggleClass('assistance-overlay-active');
$('#new-car').toggleClass('assistance-active-body');
var carOfferCta = $('#new-car-offer-cta');
if (carOfferCta.length) {
carOfferCta.toggleClass('assistance-active-cta');
};
});
So that part of the function wont run unless the element is on the page. I am just wondering what is classed as the best practice. Thanks
I would advise against doing the check at all. The beauty of jQuery is that you usually don't have to know whether your selector selects 0, 1 or more elements, the methods will just work (even if working is doing nothing at all).
If you start adding these checks everywhere, you're just coupling different parts of your logic more tightly together.
(That's also why I usually prefer not to use id selectors, but select based on classes instead. If then your html changes and e.g. your jQuery code needs to act on more elements, you don't need to change anything in the structure of your page, just apply the right classes.)
jQuery already does that check for you, if the selector inside $() doesn't match any elements, the functions you chain to it won't do anything (not even produce an error). So there's really no need to check explicitly. With these exceptions:
if you want it to be absolutely obvious to anybody reading your code, that the element you're trying to create won't exist on every page that uses your script or
if you want to do a bunch of different things in your if statement,
then it makes sense to explicitly write if ($element.length).
It's better to ask if the element's val is !=undefined, rather than asking for it's length, since that way you're assuming it does exist on the page. You can add the length check right after asking for the element's existence, I usually do as follows:
if($('#new-car-offer-cta').val() != undefined && $('#new-car-offer').val().length > 0){
//do something
}

Do you need to check if a jQuery object exists?

Let's say you have a website of 100 pages and there is a div with an id of #unique-div that only appears in page-5.html, but not in the other 99 pages and to be extra simple with the example you have a JS file that is loaded on all pages, and inside it there is this:
var uniqueDiv = $('#unique-div');
uniqueDiv.addClass('random-class');
does that have any negative impact in any possible way (for instance, performance)? Would it better to do a length check first?
var uniqueDiv = $('#unique-div');
if ( uniqueDiv.length ) {
uniqueDiv.addClass('random-class');
}
If so, why?
Or what about if you are chaining objects like this:
var uniqueDiv = $('#unique-div');
someVar.add(moreVars).add(uniqueDiv).addClass('random-class');
If the object doesn't exist, what happens?
I tried looking this up, but I have always wondered this.
It is the responsibility of ANY jQuery method to have a "proper" behavior whether there are 0, 1 or more than 1 DOM objects in the current jQuery object that they are called on. So, as long as you aren't using some broken jQuery plug-in methods, you do not have to test the length before calling a method and this includes situations where you are using chaining.
So, in your case this would be perfectly fine, even if you had no idea whether #unique-div actually existed:
$('#unique-div').addClass('random-class');
If the div didn't exist, then the .addClass() method would just do nothing.
Note: that some methods that retrieve a value such as .val() are coded to only operate on the first DOM element in a jQuery object and you will have to check with an individual method like that what they are coded to return if there are no DOM objects in the jQuery object. For example, .val() will return undefined when there are no DOM objects in the jQuery object.
There might be some infinitesimal amount of performance saving, but it's really negligible. There are probably going to be plenty of times in your code you'll do a for-loop through an array, acknowledging the length might be zero.
JQuery objects always have some size to them, and all methods I know of (ie, addClass) are equipped for empty sets, so I don't see any issue with skipping the length check.

Why no error when accessing a DOM element that doesn't exist?

I have some divs with partial views in them. Why would a reference to a div that doesn't exist not show some kind of error? For example, I only have one taskN div right now:
<div id="task1">
#Html.Partial("~/Views/PartialViews/TaskDosCommand.cshtml")
</div>
This is my jQuery to show the div:
$('#task' + task.PrestoTaskType).show();
When task.PrestoTaskType is 1, the task1 div correctly displays. However, when task.PrestoTaskType is anything but 1, like 2, then nothing displays (which is good), but there is no error; no error shows on the web page, and nothing displays in the Chrome developer tools console:
Shouldn't some kind of error display when accessing a DOM element that doesn't exist?
No, because what jQuery does is .show() all elements that the jQuery object wraps. If that's no elements at all, then so be it.
That's precisely a monad-like aspect of jQuery that makes it so useful: imagine the code you 'd have to write if things didn't work that way:
var $whatever = $(...);
if ($whatever.length) $.doSomething();
This is simply worse: you need to introduce a variable (in order to avoid waste) and a conditional... for what gain exactly?
If you want to see what jQuery matched you can do that very easily with .length as above, perhaps also using .filter in the process.
One of the nice things about jQuery is that all jQuery elements return a collection, whether that is 0, 1, or many elements. This is convenient because you don't need to check the size of the collection or wrap it in an array yourself when you want to call methods on it (each for example doesn't break for 0-1 elements).
While what you're talking about is frustrating in this particular case, it is better for jQuery to work this way so you don't have to do those sorts of checks everywhere else.
If you want to branch code based on the existence of such an element, you can do this:
var task = $('#task' + task.PrestoTaskType);
if (task[0]) {
task.show();
} else {
// task not found
// take appropriate steps
}
The [0] accessor will return the first DOM element in the jQuery object or undefined if the jQuery object is empty. Since your jQuery object was constructed with an ID selector, it either contains exactly one DOM element or it's empty.

Is there a possibility to raise an event when a certain tag is created?

I want to create the equivalent of an onCreate event for all tags which will be created that match a JQuery selector.
For instance, let's consider a document where the result of $("#foo > .bar > ul > li") is an empty set. I have a function called fooBar and I want this function to be called whenever a tag matching $("#foo > .bar > ul > li") was created.
I would like to define this event in my
$(function() {});
Does somebody know a possibility for this?
As far as I'm aware there aren't any events that are fired when elements are added to the DOM, so there's nothing you can bind a handler to in order to check for this.
What you can do is set up a polling routine that will periodically check the DOM for elements that match your selector, compare the current number of matches against the previous value, and perform whatever actions you wish if they differ.
var matchedElements = 0;
function poll() {
var $elements = $("#foo > .bar > ul > li");
if($elements.length > matchedElements) {
fooBar();
}
matchedElements = $elements.length;
}
setInterval(poll, 500); // runs poll() every half a second
This all assumes that you're not controlling the creation of these elements, or at least aren't controlling them in a way that allows you to reliably know they've been created.
If the only source of these elements is a single function you've written then you could simply extend that to trigger a handler for a custom event bound in jQuery.
Most practical solution
You can hook into the DOMNodeInserted event on document to detect the changes, and use .is to check if they match the selector of your choice.
$(function() {
var selector = "whatever";
$(document).on('DOMNodeInserted', function(e) {
if ($(e.srcElement || e.target).is(selector)) {
alert("Matching element inserted!");
}
});
});​
See it in action.
Compatibility and alternatives
This approach is convenient, but it does have two drawbacks:
The DOMNodeInserted event is deprecated.
It does not work on IE < 9, and cannot be made to work.
As regards the first, I wouldn't consider this an issue. It may be deprecated, but as long as there is no other alternative I really don't think any browser vendor would pull this functionality. Maybe in five years or so this will become a practical issue, but since the code is 10 lines or so total it will surely be easy to update it to work with the latest standard.
For IE compatibility, the sad truth is that you cannot do anything directly. However, you can resort to verbose, horrible hacks that do provide results by modifying the prototypes of DOM elements. See an example tailored to work on IE8.
Sadly, there are multiple issues with this approach:
You need to fish out all the methods that can result in the DOM being modified (or at least, all of those you will be using) and weave them into the solution. In the future you will be obliged to check if new DOM mutation methods are added and keep up with supporting them.
You need to be extra careful to provide correct replacements for the methods, for all browsers that you choose to target with this (if more than one).
Extending the DOM (in general) can be problematic. If you thought this specific method of extension is bad, consider that IE7 does not support it and on that browser you 'd have to replace methods on all elements in the DOM to make sure you hook into every possible modification.
Specifically, you cannot target all current browsers with just this code (e.g. Chrome defines these methods on Node.prototype, not on Element.prototype). Targeting future browsers should not be mentioned even if joking.
Finally, you can always decide to use polling to detect changes, as Anthony explains in his answer.
Related resources
DOMNodeInserted equivalent in IE?
Modify prototypes of every possible DOM element

is there a difference between the jquery code here?

Here is the code block a
$('ul.filter li').each(function() {
$(this).click(function(e) { //do something });
});
Here is code block b
$('ul.filter li').click(function(e) { //do something });
Don't these do the same thing? is one better than the other? Which one is the better/faster method?
I would assume block b since it has less code but I want to confirm it here, thanks
The effect you see will be the same, but in the first case, every li element is assigned a new function, as you are creating the function object inside the each callback.
In the second case, there exists only one copy of the event handler, which means it uses less memory over all (although this is probably not measurable).
Internally, click calls on (in jQuery 1.7), which is iterating over the elements via each as well:
return this.each( function() {
jQuery.event.add( this, types, fn, data, selector );
});
This is the case with many jQuery methods (most often noted in the documentation), so you are saving characters and memory by letting jQuery implicitly do this.
They would both have the same effect.
I would prefer the second only because it is more concise and you're creating a single anonymous function to handle the click rather than an anonymous function per element.
For Jquery philosophy, the second is better because it is shorter.
Both are more or less same and give the same results. The second code snippet will also internally run each loop and assign the click handler to each li element.
But yes the second code snippet is very clear and simple.
The second usage is called "implicit iteration" and is one of the cornerstones of jQuery.
For example, in JavaScript Definitive Guide, 6th Ed, p.530 for jQuery Basics:
Despite the power of the each() method, it is not very commonly used,
since jQuery methods usually iterate implicitly over the set of
matched elements and operate on them all. You typically only need to
use each() if you need to manipulate the matched elements in
different ways. Even then, you may not need to call each(), since a
number of jQuery methods allow you to pass a callback function.
in http://jqfundamentals.com/chapter/jquery-basics
Implicit iteration means that jQuery automatically iterates over all
the elements in a selection when you call a setter method on that
selection. This means that, when you want to do something to all of
the elements in a selection, you don't have to call a setter method on
every item in your selection — you just call the method on the
selection itself, and jQuery iterates over the elements for you.
Typically, when the library has this built-in as the standard way of doing it, it will be in general better and faster, or else they wouldn't have built it in.

Categories