Try Tuts+ Premium, Get Cash Back!
Quick Tip: Think Right-to-Left with jQuery

Quick Tip: Think Right-to-Left with jQuery

Tutorial Details
  • Topic: JavaScript Selector Engines
  • Difficulty: Intermediate
  • Estimated Completion Time: 5 Minutes

As English speakers, our minds are geared toward interpreting data and text from left-to-right. However, as it turns out, many of the modern JavaScript selector engines (jQuery, YUI 3, NWMatcher), and the native querySelectorAll, parse selector strings from right to left.

It’s important to note that, more often than not, you don’t need to worry about selector performance too much — as long as your selectors aren’t obnoxious. jQuery’s Sizzle is incredibly fast and accommodating.


An Example

Consider the following selector:

$('.box p');

Though some — generally older — selector engines will first query the DOM for the element with a class of box, and then move on to finding any p tags which are children, jQuery works in reverse. It begins by querying the DOM for all paragraph tags on the page, and then works it way up the parent nodes and searches for .box.

JSPerf

We can use the excellent JsPerf.com website to test this out.

// The markup
<div id="box">
  <p> Hello </p>
</div>

// The Test

//1 . 
$('#box p');

// 2.
$('#box').find('p');
JS Perf

The image above shows that using find() or children() is roughly 20-30% faster, depending upon the browser.

The jQuery library has an optimization that will immediately determine if an id was passed to the jQuery object ( $('#box') ). If that’s the case, it doesn’t need to use Sizzle; instead, it quickly passes the selector to getElementById. And, of course, if the browser is modern enough, querySelectorAll will take over for Sizzle.

On the other hand, with $('#box p'), jQuery needs to parse this string with the Sizzle API, which will take a bit longer (though Sizzle does have an optimization for selectors that begin with an id). This is precisely why it’s also marginally faster to do things like $('.elems').first() over $('.elems:first'). The latter selector will need to be parsed.


Another Example

Let’s review another example:

$('#container > :disabled');

This selector seems appropriate. Find all disabled inputs (or actually, elements) that are within #container. However, as we’ve learned, jQuery and the native querySelectorAll work right-to-left. This means that jQuery will grab, literally, every element in the DOM, and determine if its disabled attribute is set to true. Notice that there’s no pre-filtering to first find all inputs on the page. Instead, every element in the DOM will be queried.

// From the jQuery Source
disabled: function( elem ) {
    return elem.disabled === true;
}

Once it’s compiled a collection, it then travels up the chain to the parent, and determines if it’s #container. Certainly, this isn’t effective, and, though it’s true that perhaps too much attention in the community is paid to selector performance, we should still strive to not write overly intensive selectors, when possible.

You can improve this selector a bit by doing:

// Better
$('#container > input:disabled');

This code will limit the query to all inputs on the page first (rather than every element). Even better, though, we can again use the find or children method.

$('#container').children('input:disabled');

Don’t Worry Too Much

It’s important for me to reiterate that you honestly don’t need to worry about selector performance too much. There are plenty of optimizations in jQuery that will assist you. It’s generally better to focus on bigger ticket items, like code organization and structure.

As an example, if Sizzle comes across a selector like $('#box p'), it’s true that it works right-to-left, but there’s also a quick regex optimization that will first determine whether the first section of the selector is an id. If so, it’ll use that as the context, when searching for the paragraph tags.

Nonetheless, it’s always helpful to know what’s happening behind the scenes — at least at a very low level.

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://matt-bridges.com/ Matt Bridges

    Excellent article. I tend to be a server-side guy myself, but I love working with jQuery. I just found one more reason to like it more!

  • http://www.wordimpressed.com Devin Walker

    Good tip, that jS tester site is pretty sweet indeed!

  • http://www.salsainfo.pl mario

    Try to test it in Opera 11.01 – first method is the best, second 70% slower

    • http://www.jeffrey-way.com Jeffrey Way
      Author

      That’s why you shouldn’t focus too much on selector performance. It’s faster because Opera 11 will use the native querySelectorAll, whereas something like IE6 will use Sizzle.

      • http://benmartinstudios.com Ben

        I just happened to be using Opera 11 at the time of running the test (usually use FF) and I got 256,699 Ops/sec for the Right to Left. My scores were roughly 40% above the average in the Browserscope. In Chrome they were around 45-50% faster than the average. Perhaps that is due to the fact that Ive got a core i7 950.

        But one thing Im wondering why does Opera obliterate Chrome?

        Comparing #2 and #3 isnt really fair. Youd expect $(#box).find(p) to be slower than $(#box).children(p) because the latter only searches immediate children where as the former searches for children, grandchildren, etc.

        I wonder if it is worth it to make simple changes like this. Granted that it does increase performance, but it would also slow down page load (especially for mobile devices) if you employed this in a large scale project.

  • http://trifectainteractive.com Judd

    Nice tip! CSS selectors are parsed the same way.

  • http://www.neville.sk Michal Zuber

    Big thanks. Always wanted to know if find is faster.

  • http://www.techbrij.com Brij

    Good Post!!!

    We should use $(‘#box’).find(‘p’); instead of $(‘#box p’) for fast operataion ??

  • Seb

    My apologies for the Off Topic
    What about an article about jQuery 1.5?

  • Ege Özcan

    Wow… never would expected that! What’s more shocking is the huge performance gap between Opera and the others. Another interesting point is that Opera’s performance significantly drops from 11.0 to 11.01

  • http://www.maiconweb.com Maicon Sobczak

    Very interesting to know about the way that jQuery read. I’ll create the habit to use find() e children()

  • http://www.pixelsyncapp.com Bart

    This is very interesting. I like jQuery a lot, but I never looked at it like that. Once again, this shows how useful and eye-opening it can be to know what frameworks do under the hood. Thanks, Jeffrey!

  • Nathan

    I think any serious programmer will have the DOM in mind, not the end result you see in your browser. Which would still make it left-to-right.

  • http://nodecms.org/en Alexander Trefz

    Note that Prototype now also uses Sizzle.

  • http://www.drivvedwebbyra.se Fredrik

    Nice tip! need to check this out… when i get back home

  • http://forgetdbigwords.com Okeowo Aderemi

    Nice Tutorial,A Question if i must ask Nettuts has covered all Javascript library except Dojo which is quite nice and rich just a thought though.

  • Patrick

    Oh, didn’t know that. Pretty nice tip.

  • http://johannesholmberg.se Johannes Holmberg

    Nice tip Jeffrey! By the way, which plugin do you use to show code on this site?

  • http://www.maximelebreton.com maximelebreton

    Since jQuery 1.5, it seems find() is faster than children(),
    run the test again : http://jsperf.com/jquery-selector-perf-right-to-left

  • kankuro

    Hello Mr. Way, can I ask a suggestion? If there’s a time for you or the other blog author of nettuts+ can you please post an article about MooTools. A tuts about MooTools JavaScript Library…

    thanks a lot.

  • http://www.pixelflo.net Pixelflo

    Never really thought about selector performance. Thanks for the article!

  • http://chengis.me cheng

    What if I need to bind multiple selectors, like:
    $(“#container-video p, #container-nav p”)
    blah…?

    • http://www.axelnorvell.com Axel Norvell

      You can combine the two ID’s in the selector function, then use find to locate the p of both of those IDs

      $(‘#container-video, #container-nav’).find(‘p’).css(“color”,”red”);

      See the working example here:
      http://jsfiddle.net/jnUSH/

  • Levani

    Excellent article, thanks… I’ve been working with jquery for more than a year and this was really new for me. :)

  • http://www.viniciusweb.com.br/ Desenvolvedor Web

    had never thought of jquery this mode, I’m using what I learned in this post, thank you!

  • Lars

    The first example talks about ‘.box’ (the class), but the corresponding jsperf test uses ‘#box’ (the ID). I guess ‘#box’ was intended? Or maybe it doesn’t matter (but is still confusing).