Selector Engines: Right to Left

By  on  

One lessor known fact about CSS selectors, querySelectorAll, and JavaScript-based selector engines is that they read your selectors from right to left.  This news hit me as illogical at first, as you'd think that the first element in a selector string like "#myElement a.something .else" would provide a base context, but no:  the ".else" is search for first.  After more thought, searching for the right-most selector piece makes sense, as you instead collect the ".else" elements first (instead of, theoretically, all the elements under "#myElement", then "a.something" elements, and so on) and then look up the chain for matches.  Essentially, you grab all the potential matches and then confirm by walking up the DOM tree, instead of grabbing the parent and look for matches on the way down.

I was recently looking at popular development site and found the following snippet:

jQuery("#subscribe-main li:nth-child(4)")....

This snippet found the desired elements in 1ms according to FireBug's console.  A millisecond is lightning fast, but if you slightly change the selector code, you get a faster result:

jQuery("li:nth-child(4)", "#subscribe-main");

// Could use this as well
// jQuery("#subscribe-main").find("li:nth-child(4)")....

The selection code above returns the same elements in 0ms.  A millisecond difference is negligible in one instance, but in a large application, these milliseconds will add up!

This post simply acts a a reminder that selector composition is important.  Here's a task for you:  go to the sites you've written the JavaScript selectors for and compare your selectors like I have above.  If you aren't familiar with basic selector time testing via the console, here's how you do it:

console.time("someKey");
jQuery("#mySelector .more .stuff")...
console.timeEnd("someKey");

The console doesn't do better than millisecond precision, but a different result at that precision gets you started in selector enhancement.  Happy selector revising!

Recent Features

  • By
    Serving Fonts from CDN

    For maximum performance, we all know we must put our assets on CDN (another domain).  Along with those assets are custom web fonts.  Unfortunately custom web fonts via CDN (or any cross-domain font request) don't work in Firefox or Internet Explorer (correctly so, by spec) though...

  • By
    Send Text Messages with PHP

    Kids these days, I tell ya.  All they care about is the technology.  The video games.  The bottled water.  Oh, and the texting, always the texting.  Back in my day, all we had was...OK, I had all of these things too.  But I still don't get...

Incredible Demos

  • By
    Introducing MooTools Dotter

    It's best practice to provide an indicator of some sort when performing an AJAX request or processing that takes place in the background. Since the dawn of AJAX, we've been using colorful spinners and imagery as indicators. While I enjoy those images, I am...

  • By
    Elegant Overflow with CSS Ellipsis

    Overflow with text is always a big issue, especially in a programmatic environment. There's always only so much space but variable content to add into that space. I was recently working on a table for displaying user information and noticed that longer strings were...

Discussion

  1. false

    But the selectors engine made by the frameworks like mootools didn’t work like that so how do you explain that ?
    How do you make this working with mootools ?
    $$('#.classSearched #idContainer')
    because actually the normal way is : $$('#idContainer .classSearched')

    • Most select engines to do work RTL, but Moo’s current engine, Slick, does not. Slick2 will be RTL.

  2. false

    Ok nice, i’ve saw yesterday that Sizzle do RTL and just understand why your article was written with jQuery :)

  3. If this is about performance, there is a test on jsPerf: jQuery find versus context. Therefore, this would be the fastest combination of selectors: jQuery("#subscribe-main").find("li:nth-child(4)");

  4. If it is that easy (if there’s an expensive query on the right and a cheap one on the left make it work LTR) shouldn’t the engine do that optimization for you? It’s probably not always that easy to find out, though…

    • I’m not aware of browsers doing *any* optimization for you. You write crap HTML, JS, CSS — you suffer.

  5. Markus Staab

    You should mention that performance varies a lot across browsers

  6. I still don’t understand why they design Selector Engines RTL. We as human read LTR. So when we write jQuery("#subscribe-main li:nth-child(4)"), we would think that selecting the element by id, then drill down to its children would be faster (from left to right). But in reality we would have to write jQuery("li:nth-child(4)", "#subscribe-main") to achieve better performance that jQuery("#subscribe-main li:nth-child(4)") is supposed to give us in the first place! Am I missing something?

  7. Yep, providing the contextual argument would work as well.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!