4

I have a general question about jQuery. Lets say I have the following code:

<div id="container">
    <ul>
        <li>1</li>
        <li class="target">2</li>
        <li>3</li>
    </ul>
</div>

What is the best, and fastest?, way to select the target element?

$('div#container ul li.target')

or

$('#container .target')

or

$('li.target')

or is this even faster:

$('.target')

I want to know, what is the best way in achieving this? You can say the more specific the better, but too specific will slow the process down, I guess. Also the class method is "slower", but that difference isn't that big anymore, or am I wrong?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Milaan
  • 247
  • 5
  • 14

5 Answers5

6

For this example $('.target') is the fastest. JQuery has figured out traversal algorithms ;)

Proof: http://jsperf.com/jquery-selector-speed-tests

AlienWebguy
  • 76,997
  • 17
  • 122
  • 145
  • Thanks for the link! Really appreciate it! Thanks for youre answer! – Milaan Jul 26 '11 at 22:16
  • 1
    I have to disagree with your conclusion. The DOM in that example is incredibly simple, of course checking for 2 selectors is going to be slower than 1, but if it's a much more complicated tree, the benefits of an id container are likely to shine through. While that is empirical evidence, the conclusions are far far *far* too general. – davin Jul 26 '11 at 22:26
  • @davin - I'm open to that possibility. Why not make a fork of that jsperf test and add some DOM complexity to test the theory? – AlienWebguy Jul 26 '11 at 22:29
  • 1
    Careful - in IE6 .target is VERY slow. Maybe 1/10 the speed of the others. In more modern browsers all of them are within 10% of each other. – Ariel Jul 26 '11 at 22:30
  • @Milaan Just tried IE7 - .target was miserably slow. Do not use this version. Use any of the others, except .target, even if .target is fast in modern browsers, it's only by a small amount. In older browsers it's extremely slow. – Ariel Jul 26 '11 at 22:40
  • @Davin: I don't doubt that there's a chance further complexity may alter the results, but AlienWebguy clearly states '_For this example_', so personally I'd find it hard to disagree with this specific answer (older browser caveats aside). – Town Jul 26 '11 at 22:41
  • @Milaan: Don't prefix ID selectors with a tag, it's slower. [Documentation](http://api.jquery.com/id-selector/). – Town Jul 26 '11 at 22:44
  • Well the question is about the fastest way, so if there are people who are using IE6, we should take that into account. @Ariel IE7 is also slow, is that because they don't support `getElementByClass`? So the best way is `$('#container li.target')`? – Milaan Jul 26 '11 at 22:44
  • @Town, that's a snippet of the OP's DOM, not the entire thing. How can you conclude how selection on the whole will perform when you don't know what the whole is? – davin Jul 26 '11 at 22:45
  • @Davin: Fair point, you can't. However, it's only really possible to answer questions based on the detail available, which in this case is that markup. The OP doesn't (unless I've missed it in the comments) state that this is a snippet of something larger. The older browser comments are duly noted though, Akkuma makes a good point below. – Town Jul 26 '11 at 22:52
  • @Town, i'm sorry I did not specify that, but I thought that it was reasonable to believe that this just is a part of something a lot bigger. Because the speed would be affected not much if that was the only HTML available. So: It's a piece of a whole site (how big is that? Well, take the average). – Milaan Jul 27 '11 at 12:22
2
  • Id's need no extra descriptors. #id is better than div#id.
  • Sizzle usually works right-to-left (apparently when there's an id context on the left it searches that first), so it's better to be more specific on the right (so that fewer results remain and less secondary checks are run). However with modern browsers implementations of querySelectorAll may not hold to that, so really it's difficult to tell.

On the whole it's difficult to tell, and for the most part changes will be micro-optimisations. How many selections are you doing anyway that it makes such a difference? More importantly, if your execution is slow, have you profiled it and clarified where the bottleneck is? If not, you're probably squeezing very hard and are not going to get much lemon juice out of this one.

Finally, you have to realise as many jsperfs or other benchmarks that people throw at you, it is inconclusive to the general case. It's all very case specific. Maybe your DOM is simple, maybe in the future it will be more complicated. You need to test your own case. Another answer links to a solution whose conclusion I totally disagree with. There, the DOM has 4 elements, in a real-world case, it might have 4000 which means all bets are off.

davin
  • 44,863
  • 9
  • 78
  • 78
  • +1 for `#id` is better than `div#id`: From [jQuery ID Selector](http://api.jquery.com/id-selector/): _For id selectors, jQuery uses the JavaScript function `document.getElementById()`, which is extremely efficient. When another selector is attached to the id selector, such as h2#pageTitle, jQuery performs an additional check before identifying the element as a match._ – Town Jul 26 '11 at 22:20
1

In new browsers it doesn't matter, they are all the same, but in older ones this:

$('#container li.target');

Seems best to me. The #container gets directly found with getElementById(), then getElementsByTagName() is used for the li, and finally jquery manually checks the .target class on the results.

Ariel
  • 25,995
  • 5
  • 59
  • 69
  • @Arial can you please do browser comparisons on this page http://jsperf.com/jquery-selector-speed-tests and post is fastest in which browser? That would be great to know! – AlienWebguy Jul 26 '11 at 22:16
  • I tried it in IE6 and #container li.target was indeed fastest (1946). Slowest by a LOT was .target (228). – Ariel Jul 26 '11 at 22:28
  • @AlienWebguy just tried IE7 with the same results - .target was miserably slow. li.target or #container li.target were best. – Ariel Jul 26 '11 at 22:39
  • Well, that's definitely good to know, but I stand by using current best practices to phase out legacy software. If we keep accommodating old junk browsers we hinder our own progress as developers. Besides, slow isn't broken. Slow encourages upgrades :) – AlienWebguy Jul 26 '11 at 22:48
1

http://jsperf.com/jquery-selector-speed-tests/3

If you test this in IE7 you'll find the advise of $(.target) is incorrect there. You want to optimize for the worst browser you support in my opinion, which means $('#container').find('.target') is actually faster than the current recommendation.

Edit: http://jsperf.com/jquery-selector-speed-tests/7 shows that in many cases .target isn't going to suffice as you'll need some more specific context.

Akkuma
  • 2,215
  • 3
  • 16
  • 21
  • You're saying that you'd optimize youre site's for the slowest users? Eventhough there might be only 1% of youre users using IE7? – Milaan Jul 26 '11 at 22:34
  • 1
    @Milaan the difference in modern browsers is very small, within the error margin of the test. But in older browsers .target is pathologically slow, so avoid that one, use any of the others. – Ariel Jul 26 '11 at 22:43
  • 1
    If you look at the benchmarks you'd find that in something like Chrome we are looking at a difference of 10k between the two top options. The worse choice in Chrome is still faster than the best choice in IE. Using the top choice in IE compared to what is "recommended" is a 10x difference. You really want to penalize users to that extreme, when your Chrome users aren't going to see a difference between the two? IE7 makes up way more than 1% of most visits, http://marketshare.hitslink.com/browser-market-share.aspx?qprid=2. If they aren't on your site, well I wish I was in your shoes. – Akkuma Jul 26 '11 at 22:43
  • So you're saying: use the fastest one of the slowest browser?: `$('#container li.target')`? – Milaan Jul 26 '11 at 22:46
  • @Milaan yes, because in the faster browsers the difference is so small that it barely matters. But in older browsers it matters a lot. – Ariel Jul 26 '11 at 23:01
  • Do realize the entire Internet experience sucks for people on legacy IE. Sacrificing coding best-practices to accommodate these browsers is shameful. I'm not saying we should use practices which don't render at all in IE7, but seriously the world will only change if we stop spoon-feeding people who haven't upgraded yet. – AlienWebguy Jul 27 '11 at 13:46
  • @AlienWebguy I'd love to live in a world where I could do that, but I work in a company that supports IE7 as do many others. I prefer clients not complaining about performance issues, which costs us referrals and their possible future business. I'd also call it a serious stretch to call it a coding best practice when I've already shown in the other jsperf test that using `$('.target')` won't suffice in many cases. – Akkuma Jul 27 '11 at 16:33
0

Well, it depends on the situation what you are trying to achieve.

Ok, if you use a selector like this [id *= 'someid'], jQuery will look in the complete DOM that matches the specific selector. So which will take a lot of time. I useally solve this by adding a class (which is unique) for that particular element, or I must be sure that I can obtain the element via the id.

Another example, if you are going to use multiple selectors (which is about your question) it will read each selection you made in your selector. We take for example the first one:

$('div#container ul li.target')

First jQuery is going to match each div that have the id container. After that in each container jQuery is going to search for an UL list. After that it is going to search for an LI with the class target. In this particular situation it is going to read 3 times through the DOM(but still in a smaller subset, and don't forget it reads top to bottom).

So based on this explanation the last one

$('.target')

Will be the fastest. Why? jQuery needs to look up in the DOM one time that matches the selector.

Michiel Peeters
  • 390
  • 3
  • 13
  • 1
    Indeed you are right, but there was a time when you wanted to get a element by `class` was much slower then getting the element by `id`. So thats the reason I'd asked. But you're right, especially for big websites! Thank you for explaning! – Milaan Jul 26 '11 at 22:26
  • It doesn't read three times through the DOM. It reads once to find the id'd element and make sure it's a div (we'll assume valid markup), and then within that element once more. That is a total of less than 1 (i.e. once, maximum). On the contrary, your example reads one entire traversal of the DOM, since class elements are not unique by definition, as such it must read the entire DOM, whereas the first example may read a small fraction. – davin Jul 26 '11 at 22:31
  • Well thats what ive said isnt it? :) or does it really read in one go? I meant with my explanation that it must read again in the subset that he has already matched. But anyway thanks for clarifying :) – Michiel Peeters Jul 26 '11 at 22:36