querySelectorAll in Firefox 3.1

A brand-new implementation of the Selectors API has landed in the latest Firefox nightlies (and in Firefox 3.1a1) – on track to head your way in the upcoming Firefox 3.1 release.

I’ve talked about this API before (1, 2) and while I do have some misgivings about the current API (which will be remedied in upcoming revisions of the spec) there is one thing that is undeniable about it: It is extraordinarily fast.

Thankfully, implementations haven’t scarified specification compatibility for performance and we can see both the Firefox and WebKit implementations coming in at 99.3% passing the Selectors API test suite. Opera is working on their implementations, slated for Opera 10, and Microsoft has an implementation in beta 1 of Internet Explorer 8. This means that by late this year all browsers will have an implementation of the Selectors API in the market.

JavaScript libraries have already been working to utilize this new API, preparing for when it’ll eventually be ready for all to use. The current score is:

  • Dojo has querySelectorAll support in Dojo 1.1.1, although support for Safari 3.1 is disabled (there were troublesome crashing bugs in early versions of Safari 3.1 that have since been resolved).
  • Prototype has querySelectorAll support in their Git repository (presumably to be rolled into their next release).
  • jQuery has querySelectorAll support in an experimental plugin (to land in the next release).

This has lead to some interesting numbers (utilizing the same testing techniques employed by the WebKit team):

Library Time (ms)
Prototype 1.6.0.2 44677
Prototype Git 9914 (123% slower than native, 351% faster than DOM)
jQuery 1.2.6 35045
jQuery 1.2.7 Plugin 4731 (7% slower than native, 641% faster than DOM)
Dojo 1.0.2 20782
Dojo 1.1.1 5669 (28% slower than native, 267% faster than DOM)
Native 4441

That means that libraries that utilize querySelectorAll will be running 2-6x faster than their previous versions. This is already quite impressive.

There are two points to consider when using this API:

  1. That you need to try and keep the overhead on top of the querySelectorAll method as low as possible.
  2. That it becomes advantageous to avoid the querySelectorAll API in some extreme cases (for example, jQuery avoids it for #id queries, allowing it to go over 10x faster than querySelectorAll).

A lot of bare-bones selectors library implementations are going to look something like this:

function querySelectorAll(selector){
  try {
    return Array.prototype.slice.call(
      document.querySelectorAll( selector ) );
  } catch(e){}
  
  return myOtherLibrary( selector );
}

Note two points: There’s a try-catch block there to capture any syntax errors that are generated by querySelectorAll (syntax errors could be generated by APIs that the implementation doesn’t understand – like jQuery’s div:first, for example). If no exception is thrown while retrieving the results we need to convert it into an array (most libraries convert result sets into arrays – or bless them in some manner).

Tackling both of these points will introduce some level of overhead in a library (on top of the native querySelectorAll implementation). Of course it’s never as simple as it should be, many libraries extend these return sets with additional functionality so the overhead will be that much greater.

Regardless it’s readily apparent that this API will be quite instrumental in trivializing one of the most difficult parts of implementing a new JavaScript library. Everything after this is just gravy.

Posted: August 20th, 2008


Subscribe for email updates

20 Comments (Show Comments)



Comments are closed.
Comments are automatically turned off two weeks after the original post. If you have a question concerning the content of this post, please feel free to contact me.


Secrets of the JavaScript Ninja

Secrets of the JS Ninja

Secret techniques of top JavaScript programmers. Published by Manning.

John Resig Twitter Updates

@jeresig / Mastodon

Infrequent, short, updates and links.