jQuery: Performance analysis of selectors
jQuery is one of the best javascript libraries I have ever worked with. It is quite simple and usually reduces the amount of javascript code in web applications. Besides all the advantages it brings to the web community, we have to pay attention to a few details when reusing this library in order to get the best part of it.
I will describe a few tips on how to improve the performance of your javascript code. Most of these tips aren't a big deal if your pages are small and simple. But if you create complex html pages, you should follow them in order to prevent the browser from locking when the page loads up. Even if you are not concerned with performance at all, learning these things will bring benefits to your overall knowledge about this library.
jQuery has a bunch of different selectors - as you can see from the documentation here - and I won't consider all of them in this analysis. My idea is to explore different ways of finding a specific element in the html page by using different types of search. Benjamin Sterling has already researched this topic here, but I want to add a different point of view, which is more accurate and provides more alternatives for the developers.
Let me start talking about the test html page, which has over 20,000 elements separated in chuncks of 5,000. Basically, there are 5,000 <div> + 5,000 <span> + 5,000 <p> and 5,000 <small> with the following characteristics:
<div id="a-1" class="div-1" row="a-1">Div 1</div>
...
<div id="a-4999" class="div-4999" row="a-4999">Div 4999</div>
<span id="b-1" class="span-1" row="b-1">Span 1</span>
...
<span id="b-4999" class="span-4999" row="b-4999">Span 4999</span>
<p id="c-1" class="p-1" row="c-1">Paragraph 1</p>
...
<p id="c-4999" class="p-4999" row="c-4999">Paragraph 4999</p>
<small id="d-1" class="small-1" row="d-1">Small 1</small>
...
<small id="d-4999" class="small-4999" row="d-4999">Small 4999</small>
The 20,000 elements above are generated by javascript. They provide a good laboratory to check the speed of each selector, since we can expect more accurate results in this analysis. The page itself has a few more tags to display information and execute each test, but this shouldn't affect the final results.
Test 1 - Finding an element by ID
The first test is to find an element by its ID. I decided to get the element whose ID is "d-2642". We can do this in three different ways:
A - Using the ID selector
$('#d-2642').html()
B - Using attribute search
$('[id="d-2642"]').html()
C - Using attribute search + tag
$('small[id="d-2642"]').html()
Running this test for the first time in different browsers, I could reach the results displayed in the following table (in milliseconds). It is clear that the # operator is the most efficient one.
| Firefox | Opera | IE6 | IE7 | Safari | |
|---|---|---|---|---|---|
| A | 156 | 0 | 31 | 40 | 0 |
| B | 3609 | 828 | 2344 | 841 | 844 |
| C | 578 | 93 | 406 | 210 | 172 |
Test 2 - Finding an element by Class
Although CSS classes are intended to be reused among elements, you might create some elements with a unique class name just to identify and retrieve them through javascript. This is exactly what we test in this second test by seaching the element whose class is "p-4781". We have four alternatives:
A - Using the class selector
$('.p-4781').html()
B - Using the class selector + tag
$('p.p-4781').html()
C - Using attribute search + tag
$('p[class="p-4781"]').html()
D - Using tag search + filter
$('p').filter('.p-4781').html()
After running this test for the first time in different browsers, I got:
| Firefox | Opera | IE6 | IE7 | Safari | |
|---|---|---|---|---|---|
| A | 2891 | 641 | 1718 | 631 | 329 |
| B | 453 | 78 | 313 | 180 | 78 |
| C | 422 | 109 | 578 | 201 | 187 |
| D | 203 | 266 | 375 | 210 | 94 |
The table above shows case B as the fastest selector for most browsers (except Firefox). It is easy to understand why case A isn't efficient, since the code has to iterate over all elements of the DOM tree. Case C and D aren't that bad, but I would say case B should be the preferred one for this goal.
Test 3 - Finding an element by Attribute
The third test is to find elements by a given attribute value. Sometimes this technique is useful and I decided to test it by searching for the element that has "c-3221" as the value of the row attribute. There are four possibilities in this case:
A - Using attribute search
$('[row="c-3221"]').html()
B - Using attribute search + tag
$('p[row="c-3221"]').html()
C - Using tag search + filter
$('p').filter('[row="c-3221"]').html()
D - Using (tag + attribute) search + filter
$('p[@row]').filter('[row="c-3221"]').html()
Running this test for the first time in different browsers, I could get the following results:
| Firefox | Opera | IE6 | IE7 | Safari | |
|---|---|---|---|---|---|
| A | 6610 | 1390 | 2500 | 801 | 1156 |
| B | 1250 | 360 | 406 | 200 | 250 |
| C | 1265 | 968 | 485 | 231 | 281 |
| D | 4078 | 1000 | 656 | 320 | 625 |
Case B is for sure the best approach to find an element by a specific attribute value.
Conclusions
This experiment was an attempt to find the right and fastest way of finding specific elements in the DOM tree using jQuery. Every jQuery developer should know and understand these details in order to create efficient code. Benjamin Sterling gave the first step in such performance analysis, but I could provide another point of view, which uses a big DOM tree and more alternatives to be checked.
Comments
Also, $('p[@row]') should be $('p[row]'), the @ at depreciated in 1.2, it can be still used, but I think they will be getting rid of it all together in the next release, which should be sometime this month (I can't find the email that verified the date).
Over all, really nice post. I am going to bookmark so I can refer back to it.
Thank you for your opinion and feedback.
As I said, this is just a complement for your nice work. :-)
I didn't know about the issue with the @ operator -- this is why I didn't find documentation about it in the official website (only in my books). :-))
Let's keep up the good work!
1. define new var a this var use in function
var $inputs = $("#mform input");
function a1(){ $inputs... }
...
function a10(){ $inputs... }
2. or in each function call new selector
function a1(){ $("input")... }
...
function a10(){ $("input")... }
Creating a reference to $('#mform input') will increase performance because jQuery is only called once. However, the trade-off is that you'll have more global variables, and more objects stored in memory. For something like $('#id'), I'd recommend calling it every time, since the native document.getElementById('id') will be used. However, if out of necessity, you must call $('.className') in every function, I might recommend assigning it to a variable, since the jQuery call for a class will be much slower.

Stumble it!
Dzone.com
Digg.com
Reddit.com
Del.icio.us