Possible Duplicate:
Performance of jQuery selector with context
In the jQuery DOCS it says
By default, selectors perform their searches within the DOM starting at the document root. However, an alternate context can be given for the search by using the optional second parameter to the $() function.
Based on that my understanding is that a selection using a context
passed in as the second parameter should be faster then the same selection without the context
passed in. However I ran some tests and it seems as if this isn't the case, or at least isn't always the case.
To elaborate, I originally wanted to see if searching for multiple elements at once ($("div1, #div2")
) was faster then searching for the two separately ($("#div1") $("div2")
). I then decided to test it with the context
and without to see how much faster it was with the context
, but was surprised when it turned out that the context
seemed to be slowing it down.
For example given the following basic HTML markup
<div id="testCnt">
<div id="Div0"></div>
<div id="Div1"></div>
<div id="Div2"></div>
<div id="Div3"></div>
<div id="Div4"></div>
<div id="Div5"></div>
<div id="Div6"></div>
<div id="Div7"></div>
<div id="Div8"></div>
<div id="Div9"></div>
</div>
And the following JavaScript (jQuery 1.8.2, and tested using FireBug)
$(function () {
var $dvCnt = $('#testCnt');
var dvCnt = $dvCnt[0];
console.time('Individual without cache');
for (var i = 0; i < 10000; i++) {
$('#Div0').text('Test');
$('#Div1').text('Test');
$('#Div2').text('Test');
$('#Div3').text('Test');
$('#Div4').text('Test');
$('#Div5').text('Test');
$('#Div6').text('Test');
$('#Div7').text('Test');
$('#Div8').text('Test');
$('#Div9').text('Test');
}
console.timeEnd('Individual without cache');
console.time('Individual with $cache');
for (var i = 0; i < 10000; i++) {
$('#Div0', $dvCnt).text('Test');
$('#Div1', $dvCnt).text('Test');
$('#Div2', $dvCnt).text('Test');
$('#Div3', $dvCnt).text('Test');
$('#Div4', $dvCnt).text('Test');
$('#Div5', $dvCnt).text('Test');
$('#Div6', $dvCnt).text('Test');
$('#Div7', $dvCnt).text('Test');
$('#Div8', $dvCnt).text('Test');
$('#Div9', $dvCnt).text('Test');
}
console.timeEnd('Individual with $cache');
console.time('Individual with DOM cache');
for (var i = 0; i < 10000; i++) {
$('#Div0', dvCnt).text('Test');
$('#Div1', dvCnt).text('Test');
$('#Div2', dvCnt).text('Test');
$('#Div3', dvCnt).text('Test');
$('#Div4', dvCnt).text('Test');
$('#Div5', dvCnt).text('Test');
$('#Div6', dvCnt).text('Test');
$('#Div7', dvCnt).text('Test');
$('#Div8', dvCnt).text('Test');
$('#Div9', dvCnt).text('Test');
}
console.timeEnd('Individual with DOM cache');
console.time('Multiple without cache');
for (var i = 0; i < 10000; i++) {
$('#Div0,#Div1 ,#Div2 ,#Div3 ,#Div4 ,#Div5 ,#Div6, #Div7, #Div8, #Div9').text('Test');
}
console.timeEnd('Multiple without cache');
console.time('Multiple with $cache');
for (var i = 0; i < 10000; i++) {
$('#Div0,#Div1 ,#Div2 ,#Div3 ,#Div4 ,#Div5 ,#Div6, #Div7, #Div8, #Div9', $dvCnt).text('Test');
}
console.timeEnd('Multiple with $cache');
console.time('Multiple with DOM cache');
for (var i = 0; i < 10000; i++) {
$('#Div0,#Div1 ,#Div2 ,#Div3 ,#Div4 ,#Div5 ,#Div6, #Div7, #Div8, #Div9', dvCnt).text('Test');
}
console.timeEnd('Multiple with DOM cache');
});
Here's a jsbin
I'm getting something like the following results
Individual without cache: 11490ms
Individual with $cache: 13315ms
Individual with DOM cache: 14487ms
Multiple without cache: 7557ms
Multiple with $cache: 7824ms
Multiple with DOM cache: 8589ms
Can someone shed some insight on whats going on? Specifically why the search is slowing down when the jQuery context is passed in?
EDIT:
Most of the anwsers here (as well as Performance of jQuery selector with context) basically say that that either the DOM in this example is too small to really gain much or that selecting by ID
is going to be fast regardless. I understand both points, the main point of my question is why would the context
slow down the search, the size of the DOM
shouldn't make a difference for that, and neither should the fact that searching by ID is already very fast.
@pebble suggested that the reason that its slower is because jQuery can't use the native browser methods (getElementByID
), this seems to make sense to me, but then why is it faster to search for multiple elements in one selection?
Anyway I dumped the tests into a jsPerf adding cases to search by class and was again surprised to see that the search for multiple classes with a cache this time was the fastest.
I would imagine there are lots of situations where using context will slow things down, mainly because jQuery will try and use browser native methods where it can - rather than traverse the entire dom. One example of this would be using
document.getElementById
as in your example.why the slow down?
getElementById
only exists on the document object - you have no way of using this on a contextual element - i.e.element.getElementById
. So my theory would be that jQuery first does the id request usingdocument.getElementById
, and then, if there is a context set - scans through the parents of each element to tell if any of them exist as children of the context - thereby slowing the process down.Other examples of selectors that may be slow
You will also find other places where depending on the selector you are using you will get performance increases - all down to what methods jQuery can use to speed up it's work. For example:
Would most likely translate to using
getElementsByClassName
or any other native method offered to select by className, However:Wouldn't be able to use this (as it has to take the relationship into account) and would have to use a mixture of
querySelector
(if it exists) and or pure javascript logic to work things out.Having a good knowledge of what native methods are available will help you optimise your jQuery queries.
Ways to optimise
If you wish to optimise using a context, I would imagine this would prove a faster query than without:
This will be because
getElementsByTagName
has existedsince the dawn of timefor a while, and can be used in pure JavaScript directly on a DOM element. However if you are to do this, it may be quicker to do the following:or
Mainly because you cut down on the jQuery function calls, although this is much less succinct. Another thing to be aware of with regards to many of the popular JavaScript environments - calling a function without arguments is a lot faster than calling with.
A relatively unused method for optimising certain jQuery selectors is to use the jQuery
eq
pseudo selector - this can speed things up in a similar way to usingLIMIT 0,1
in SQL queries - for example:Would scan inside all H2s looking for A elements, however if you know from the start that there is only ever going to be one A tag within your H2s you can do this:
Plus if you know there is only ever going to be one H2 - the logic is the same:
The difference between $().pushStack and $().add
In response to Jasper's comment here is the difference between the two functions:
.add:
.pushStack:
The major difference is that
.add()
uses.pushStack()
to acheive it's goals - add allows support for a lot more data types - even jQuery objects. Whereas.pushStack
is only designed for DOM Elements, which makes it more optimal if that is what you are using :)A quicker way to select by ID?
This is obvious, but I thought I'd put this here as sometimes things are missed - a quicker way to select an element by id would be to do the following:
All because there is no way jQuery/Sizzle can out-do a native method, and it also means you avoid any string parsing on jQuery/Sizzle's part. It's no where near as neat as it's jQuery counterpart though, and probably wont gain that much speed increase, but it is worth mentioning as an optimisation. You could do the following if you were to use ids often.
The above would be slightly slower that my previous example, but should still out-do jQuery.