Performance of jQuery Selectors with ID - javascript

I know in jQuery if we use ID to select elements,it's so efficient.I have a question about this selectors:
please consider this 3 selectors:
$('#MyElement')
$('#Mytbl #MyElement')
$('#Mytbl .MyClass')
which one is faster and why?How I can check the time elapsed to select my element in jQuery?

A direct ID selector will always be the fastest.
I've created a simple test case based on your question...
http://jsperf.com/selector-test-id-id-id-id-class
Selecting nested ID's is just wrong, because if an ID is unique (which it should be), then it doesn't matter if it's nested or not.

this is the way to stop times between some javascript calls
selectorTimes = [];
var start = new Date().getTime();
$('#MyElement')
selectorTimes.push(new Date().getTime()-start);
start = new Date().getTime()
$('#Mytbl #MyElement')
selectorTimes.push(new Date().getTime()-start);
start = new Date().getTime()
$('#Mytbl .MyClass')
selectorTimes.push(new Date().getTime()-start);
console.log(selectorTimes);
i think the second selector is not efficient, if you have a domid select this directly:
$('#MyElement')

The first one is the fastest, simply because it only has 1 property to look for. However,
document.getElementById("MyElement")
is even faster, though. It's native javascript, and unlike jQuery, the browser immediately knows what you want to do, instead of having to run through a load of jQuery code to figure out what it is you're looking for, in the first place.
You can use jsPerf to run a speed test, to compare the functions: Test Case. Results:
$('#MyElement')
Ops/sec: 967,509
92% slower
$('#Mytbl #MyElement')
Ops/sec: 83,837
99% slower
$('#Mytbl .MyClass')
Ops/sec: 49,413
100% slower
document.getElementById("MyElement")
Ops/sec: 10,857,632
fastest
Like expected, the native getter is the fastest, followed by the jQuery getter with only 1 selector at less than 10% of the native speed. The jQuery getters with 2 parameters don't even get close to the operations per second of native code, especially the class selector, since classes are usually applied to multiple elements, compared to ID's. (Native ID selectors stop searching after they've found one element, I'm not sure if jQuery does, too.)

A couple things:
More selectors = slower search. If you can get your desired result with fewer predicates, do so.
Getting an element by ID is quicker than getting by a class. getElementById is a core function of JavaScript that is heavily optimised because it is used so frequently. Leverage this when possible.
The space selector (' ') is much more costly than the child selector ('>'). If you can use the child selector, do so.
These rules apply to CSS as they do JavaScript and jQuery.

Here you are. See the comments on top of each example:
//fastest because it is just one id lookup: document.getElementById("MyElement") with no further processing.
$('#MyElement')
//a little slower. JQuery interprets selectors from right to left so it first looks up for #MyElement and then checks if it is hosted in #Mytbl
$('#Mytbl #MyElement')
//the slowest of all. JQuery interprets selectors from right to left so it first finds all elements with .MyClass as their class and then searches for those hosted in #Mytbl.
$('#Mytbl .MyClass')
If you can, always use just the id (like the first example) but if you have to have multiple selectors and classes chained together, try to put the most strict one at the right. For example an id. Because JQuery interprets selectors from right to left.

Also, if you need nested selectors, it's faster to use $().find().find()
http://jsperf.com/selector-test-id-id-id-id-class/2
$('#Mytbl .MyClass')
$('#Mytbl').find('.MyClass')
The latter is about 65% faster.

Obviously the first one, $("#MyElement") is faster than other 2.
Accessing an element with its id always faster but sometimes we had to find some element in some container. In that case we prefer .find() or .filter() (depending upon situation).
The difference between selectors depends on browser to browser. e.g. if you access through class on IE it would be slower than FF. FF is faster when accessing an element with class rather than ID.
In your second example, i.e. $("#mytbl #MyElement"), here you are finding #MyElement under #mytbl which is legal but not appropriate way. Since you already know the ID of the element (assuming you have only one element with this id on your page) so its better to use $("#MyElement"). $("#mytbl #MyElement") will read #mytbl first and traverse to find #MyElement under it hence time consuming and slow.
To test different cases you can write small snippet to read/access atleast 10000 elements in a loop otherwise it would be hard to determine which way is faster.

The fastest will be:
$('#Mytbl', '#MytblContainer' );
because in this case, jquery don't have to search the whole dom tree to find '#Mytbl'. It will search in the scope given only. IE it will serach in '#MytblContainer' only .

I would say that the first one is the fastest because you're simply searching for one id.
And
$('#Mytbl .MyClass')
is the slowest because you're not specifying the type of element that has the class "MyClass"

Related

jQuery filter efficiency

I recently came across this question: How do I check if an element is hidden in jQuery?
The currently accepted answer seems to indicate that $('element').is(':hidden') is preferable to $('element:hidden') because the :hidden filter is only being applied to a single element.
But... Calling .is() also adds extra overhead as you are calling an another function.
This got me thinking, does the above reasoning for using .is() hold true if the selector is a set of elements?
And what about more extreme cases? Take the following test case:
$('element.class1:not(.class2):visible[rel="foo"]')
Is it best to leave those in the selector? Or move them all into a single filter call:
$('element').filter('.class1:not(.class2):visible[rel="foo"]')
Or is it better to chain them?
$('element').is('.class1').not('.class2').is(':visible').filter('[rel="foo"]')
The currently accepted answer seems to indicate that $('element').is(':hidden') is preferable to $('element:hidden') because the :hidden filter is only being applied to a single element.
I don't read it that way at all, and assuming you're dealing with a string, then $("some-selector:hidden") isn't applied only to a single element; it's applied to all elements matching some-selector.
Or is it better to chain them?
$('element').is('.class1').not('.class2').is(':visible').filter('[rel="foo"]')
That will fail, since is returns a boolean:
.is(selector)
Description: Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
If you want to select elements matching multiple criteria, it's fine to put those criteria in the selector string. The only real question you should ask yourself is whether to use :hidden and other jQuery-isms in the string, since it means that jQuery can't pass the selection off to the browser's built-in (read: fast) selector engine, and has to do the work itself.
For that reason, with that example, you might prefer to move the jQuery-isms out of it:
$('element.class1:not(.class2)[rel="foo"]').filter(":visible")

jQuery methods Vs jQuery selectors

I was recently assigned a very small but complex task in jQuery, the requirement was quite simple, given the following HTML :
<div>
<span id="myid2151511" class="myclass23462362">....foobar....</span>
<span id="myid2151512" class="myclass23462362">....YoLO....</span>
<span id="myid2151513" class="myclass23462362">....lalal....</span>
<span id="myid2151514" class="myclass23462362">....foobar....</span>
</div>
What i have to do i recursively go through all the span under div, With a certain id and check if the values contained in the spans is foobar, So i can up with the following jQuery code:
$(function(){
$('div [id^="myid"]:contains("foobar"):last').css({'background' : 'rgb(227, 216, 22)' })
});
FIDDLE HERE
Its quite a complex bit of code by itself, but the jQuery documentation made it a cakewalk for me as for as understanding the code is concerned.
By now i am comfortable writing code like so in jQuery:
$('some-Element').somemethod().anothermethod().yetanothermethod();
Every function returns a value in the above jQuery statement, so chain ability becomes a reality.
but when i see code like so.
$('div [id^="myid"]:contains("foobar"):last').css({'background' : 'rgb(227, 216, 22)' });
I am thrown a bit off the hook(although i managed to write the above line myself), notice how alot of the filtering is done by a selector :last and :contains, to me they appear to be working much like some kind of a jQuery method. So my question is, how do these selectors in jQuery work in comparison to jQuery methods ?
If anybody could explain or give me a vague idea, it would be Fantastic.
EDIT ::
well to clarify my question in one line, to me $(".someClass").eq('10'); makes sense, but somehow $(".someClass:eq(10)") does't , i mean it works, but how on earth is it implemented internally ?(I wrote this edit after reading the answers below, and well this question has been thoroughly answered by now, but this edit is just to clarify my question.).
That's an interesting question. The short answer is they both accomplish the same thing. Of course though, there's always more to the story. In general:
$('div [id^="myid"]:contains("foobar"):last').css({'background' : 'rgb(227, 216, 22)' });
Is equivalent to:
$("div").find("[id^='myid']").filter(":contains('foobar')").last().css({'background' : 'rgb(227, 216, 22)' });
Most of the time when you call $(), jQuery is calling document.querySelectorAll(). This is a browser implemented function that grabs elements based on a selector. That complex string you create is passed to this method and the elements are returned.
Naturally, things implemented by the browser are faster than JavaScript so the less JavaScript and more C++, the better. As a result, your example passing everything as a selector is likely to be faster as it just sends it all to the browser as one call and tells it "do it." Calling $(), contains(), last() on the other hand is going to call querySelectorAll multiple times and therefore it will likely be slower since we're doing more JavaScript as opposed to letting the browser do the heavy lifting in one shot. There are exceptions though. JQuery generally calls querySelectorAll. However, there are times when it doesn't. This is because jQuery extends what querySelectorAll is capable of.
For example, if you do something like $(".someClass:eq(10)") per the jQuery documentation:
jQuery has extended the CSS3 selectors with the following selectors. Because these selectors are jQuery extension and not part of the CSS specification, queries using them cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. To achieve the best performance when using these selectors, first select some elements using a pure CSS selector, then use .filter().
So in that case, while $(".someClass:eq(10)") might seem to be faster, in reality $(".someClass").eq(10) or $(".someClass").filter(":eq(10)") is going to be faster since the first call will be executed as JavaScript code. The latter two will first call querySelectorAll to select by class, then only use JavaScript to find the 10th element. When jQuery has to do the selection in pure JavaScript, it does it using the Sizzle engine which is fast, very fast, but not faster than native code in the browser. So again, the short answer is, they're the same thing, the long answer is, it depends. If you're interested in all the extensions that fall into that category, the link to the jQuery documentation I included lists them.
First of all, yes nikhil was right. ID is unique identifier and can be only used once. If you are willing to apply same styles to several elements, or you to use it to select several elements together use class attribute. But however, i couldn't understand your question. But maybe this could help
there is function in javascript which is widely supported by almost all major browsers
document.querySelectorAll("div [id^=myId]");
in fact you could write your own library (well not as advanced one like jquery but)
var $ = function(selector){
return document.querySelectorAll(selector);
}
// and then you could use it like this
var elementsWithMyId = $("div [id^=myId]");
// where elementsWithMyId will contain array of all divs which's id start with myId
so as i understood your question, No. there is no magic happening behind jQuery selections it's just browser built in function which is kinda shortened by jquery. of course they added tons of new features, which would work like this:
var $ = function(selector){
var elementsArray = document.querySelectorAll(selector);
elementsArray.makeBlue = function(){
for(var i = 0; i < elementsArray.length; i++){
elementsArray[i].style.backgroundColor = "blue";
}
// so elementsArray will now have function to make all of its
// div blues. but if you want to have chain like that, we have to return this array not just make all of it blue
return elementsArray;
}
elementsArray.makeRed = function(){
for(var i = 0; i < elementsArray.length; i++){
elementsArray[i].style.backgroundColor = "red";
}
return elementsArray;
}
return elementsArray;
}
// so now you can use it like this
// this returns array which has options make blue, and make red so lets use make blue first
// makeBlue then returns itself, meaning it returns array which has again options of making itself red and blue so we can use makeRed now
$("div [id^=myId]").makeBlue().makeRed();
and thats it!

Efficiency of repeated checks with jQuery selector

Does jQuery cache selector lookups?
I am wondering whether this code
if ($("#ItemID").length) {
doSomethingWith($("#ItemID"));
}
will be significantly slower than this code
item = $("#ItemID");
if (item.length) {
doSomethingWith(item);
}
What about if you're extracting much more of the DOM e.g., $("div") rather than just $("#ItemID")? Same answer?
External references/explanation would be helpful, rather than just opinion.
According to the jQuery Learning Center, "jQuery doesn't cache elements for you. If you've made a selection that you might need to make again, you should save the selection in a variable rather than making the selection repeatedly."
This is particularly important when your selector is slow by its nature. For example, $("#foo") makes a call under the covers to document.getElementById() which should be very fast. However, a selector like $("a[rel$='thinger']") might be significantly slower on an older browser.
Remember that jQuery supports chaining, so you don't always have to introduce a variable to save the selection, you can just chain sequential calls together. For example, in this code I don't introduce a variable just to save the result of .find("img"), rather I chain three calls to prop and one to show.
clusterBlock.find("img").prop("src", clusterElt.imageUri)
.prop("alt", clusterElt.description)
.prop("id", clusterElt.imageId)
.show();
Finally, remember that a saved selection is not updated as the DOM changes. If I get all elements with class "foo" and save that in a variable, then some other piece of code adds and removes a couple of "foo" elements, then my saved selection will be missing the new elements and contain the references to the removed elements.
This
var item = $("#ItemID");
if (item.length) {
doSomethingWith(item);
}
will be faster but marginally. doSomethingWith will go to pointer where object is stored.
Turns out people do cache selectors btw

CSS Selectors performance, DOM Parsing

My question is related to DOM parsing getting triggered, i would like to know why it's faster to use a CSS ID selector than a Class selector. When does the DOM tree have to be parsed again, and what tricks and performance enhancements should I use... also, someone told me that if I do something like
var $p = $("p");
$p.css("color", "blue");
$p.text("Text changed!");
instead of
$("p").css("color", "blue");
$("p").text("Text changed!");
improves performance, is this true for all browsers? Also how do I know if my DOM tree has been re-parsed?
Well, an #id selector is faster than class selectors because: (a) there can only be one element with a given id value; (b) browsers can hold a map id -> element, so the #id selector can work as quick as a single map lookup.
Next, the first option suggested above is definitely faster, as it avoids the second lookup, thereby reducing the total selector-based lookup time by a factor of 2.
Last, you can use Chrome Developer Tools' Selector Profiler (in the Profiles panel) to profile the time it takes a browser to process selectors in your page (match + apply styles to the matching elements.)
An ID selector is faster than a class selector because there is only one element with an ID but many elements could share a class and they have to be searched.
The code below is needlessly parsing the DOM twice, so of course it will be slower:
$("p").css("color", "blue");
$("p").text("Text changed!");
I encourage you to make your own performance tests whenever you have a doubt. You can get more info on how to do that here: How do you performance test JavaScript code?. Once you've tested performance on your own, you'll never forget the results.
In particular, the execution of the $() function on a given jquery selector must obtain the matching DOM nodes. I'm not sure exactly how this works but I'm guessing it is a combination of document.getElementById(), document.getElementsByTagName() and others. This has a processing cost, no matter how small it may be, if you call it only once and then reuse it you save some processing time.

Filtering elements out of a jQuery selector

I have a page that selects all the elements in a form and serializes them like this:
var filter = 'form :not([name^=ww],[id$=IDF] *,.tools *)';
var serialized = $(filter).serialize();
This works, unless the form gets around 600+ elements. Then the user gets s javascript error saying that the script is running slow and may make their browsers unresponsive. It then gives them the option to stop running the script.
I have tried running the filters separately, I have tried using .not on the selectors, then serializing them, but I run into one of two problems. Either it runs faster without the error, but also does not filter the elements, or it does filter the elements and gives me the slow script error.
Any ideas?
With 600+ elements this is going to be dead slow. You need to offer Sizzle (jQuery's selector engine) some opportunities for optimisation.
First, consider the fact that jQuery can use the natively-supported querySelectorAll method (in modern browsers) if your selector complies with the CSS3 spec (or at least to the extent of what's currently supported in browsers).
With your case, that would mean passing only one simple selector to :not instead of 3 (1 simple, 2 complex).
form :not([name^=ww])
That would be quite fast... although you're not being kind to browsers that don't support querySelectorAll.
Look at your selector and think about how much Sizzle has to do with each element. First it needs to get ALL elements within the page (you're not pre-qualifying the :not selector with a tag/class/id). Then, on each element it does the following:
(assume that it exits if a result of a check is false)
Check that the parent has an ancestor with the nodeName.toLowerCase() of form.
Check that it does not have a name attribute starting with ww (basic indexOf operation).
Check that it does not have an ancestor with an id attribute ending in IDF. (expensive operation)
Check that it does not have an ancestor with a class attribute containing tools.
The last two operations are slow.
It may be best to manually construct a filter function, like so:
var jq = $([1]);
$('form :input').filter(function(){
// Re-order conditions so that
// most likely to fail is at the top!
jq[0] = this; // faster than constructing a new jQ obj
return (
!jq.closest('[id$=IDF]')[0]
// this can be improved. Maybe pre-qualify
// attribute selector with a tag name
&& !jq.closest('.tools')[0]
&& this.name.indexOf('ww') !== 0
);
});
Note: that function is untested. Hopefully you get the idea...
Could you maybe just serialize the whole form and do your filtering on the backend? Also, why-oh-why is the form growing to 600+ fields?
use the :input selector to only select applicable elements..

Categories

Resources