A Free JavaScript Speed Boost for Everyone!

The following is republished from The Tech Times #207.

An exciting development in the world of DOM scripting is the W3C Selector API. Up until now, using the DOM Level 2 API, the only way to obtain references to HTML elements in the DOM was to use either document.getElementById or document.getElementsByTagName and filter the results manually. With the rise of CSS, JavaScript programmers asked the obvious question, “If the browser has a really fast way of selecting HTML elements that match CSS selectors why can’t we?”.

The Selector API defines the querySelector, and querySelectorAll methods that take a CSS selector string and return the first matching element or a StaticNodeList of matching elements, respectively. The methods can be called from the document object in order to select elements from the whole document or a specific HTML element to select only from descendants of that element.

To illustrate how much easier your life will be using the Selector API, have a look at this example HTML:

<ul id="menu">
  <li>
    <input type="checkbox" name="item1_done" id="item1_done"> 
    <label for="item1_done">bread</label>
  </li>
  <li class="important">
    <input type="checkbox" name="item2_done" id="item2_done"> 
    <label for="item2_done">milk</label>
  </li>
  <!-- imagine more items -->
</ul>

Our task is to check all the checkboxes for the list items that have the class “important“. Using only DOM Level 2 methods we could do it this way:

var items = document.getElementById('menu').getElementsByTagName('li');
for(var i=0; i < items.length; i++) {
  if(items[i].className.match(/important/)) {
    if(items[i].firstChild.type == "checkbox") {
      items[i].firstChild.checked = true;
    }
  }
}

Using the new selector API we can simplify it to this:

var items = document.querySelectorAll('#menu li.important input[type="checkbox"]');
for(var i=0; i < items.length; i++) {
  items[i].checked = true;
}

That’s much nicer! The methods also support selector grouping — multiple selectors separated by commas. The Selector API is working right now in Safari 3.1, the Internet Explorer 8 beta, and Firefox 3.1 alpha1. Opera is also working on adding support for the API.

If you’re a fan of one of the many JavaScript libraries available you’re probably thinking “But, I can already do that.” One of the great examples of the benefits of using JavaScript libraries are the implementations of CSS selectors found in nearly all of them. Recently we’ve seen huge speed improvements in the CSS selector implementations because library authors have been sharing their techniques. So what’s the benefit of using the Selector API? In a word: speed — native implementations are fast! And better yet all of the javascript libraries are poised to benefit. jQuery and Prototype are already developing implementations that make use of the Selector API, while The Dojo Toolkit, DOMAssistant and base2 have already made use of it.

There’s a reason why those 3 libraries were the first ones to benefit. Kevin talked about the potential problem back in Tech Times #190 in the article titled “Is Your JavaScript Library Standards Compliant?” The Selector API makes use of standard CSS selectors so if the browser doesn’t support a certain selector then you won’t be able to use it. The libraries that have already taken advantage of the Selector API are those that only supported standard CSS selectors. For those libraries, supporting the API was (almost) as easy as doing this:

if(document.querySelector) {
  return document.querySelector(selector);
} else {
  return oldSelectorFunction(selector);
}

Libraries that support custom selectors will have more work to do. The risk is that if you have used custom CSS selectors extensively in your project, it may be difficult for your chosen library to pass on the speed benefit to you because the library will have to use its default selector instead of the Selector API. If the library somehow rewires its custom selectors so that they can utilize the Selector API, the secondary risk is increased code bloat.

Hopefully the Selector API will encourage the use of standard CSS selectors over custom ones. In fact if uptake of the new browser versions is good and the performance benefits of the new Selector API are compelling enough we could see custom selector functionality moved to supplementary libraries you only need to use in case of legacy compatibility requirements.

Dean Edwards’ base2 Library has the nicest implementation in my opinion. Base2 implements the API exactly, which means you can write your JavaScript using standard the standard API methods — Base2 only creates custom querySelector and querySelectorAll methods if the browser doesn’t support them. You can’t get any cleaner than that. Base2 does, however, implement the non-standard “!=” attribute selector in it’s custom selector function, apparently because of peer pressure, so it’ll have to have points deducted for that.

Regardless of whether you use a JavaScript library or roll your own, the new browser implementations of the Selector API give everyone an instant speed boost. We all win, hooray!

Image credit: Yogi

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • Integralist

    Can you confirm which browsers will support the new W3C Selector API?

    I assume: Firefox (version?), Opera (version?), Safari (version?), IE8.

    I also assume that IE6/7 are off the list.

    Ta,

    M.

  • http://tetlaw.id.au Andrew Tetlaw

    I mentioned it above but here it is again: Safari 3.1, Firefox 3.1, and IE8. As far as Opera goes, it doesn’t appear to be in 9.6 but according to this post: http://my.opera.com/core/blog/selectors-api it is being worked on.

  • krdr

    Andrew,
    there’s pattern called state machine. Instead of checking: if(document.querySelector)on each function call, all you need is to check once, during initialization:
    if(document.querySelector) {
    loadDOMSelectorFunctions();
    } else {
    loadOldSelectorFunctions();
    }

  • elduderino

    It’s an exciting development in javascript but how long is it going to be until we can start using this and be sure it’s going to be working for everyone??

  • Mike

    This is very exciting… I wonder if this will have significant speed advantages over current frameworks with similar selector patterns like jQuery… or perhaps they already use these features when available. Ya, that would make sense.

    Either way, CSS selecting in the DOM is very cool.

  • MarkAJohnson

    This is indeed good news. It’s nice to know about Base2, too–I hadn’t seen that feature.

    To answer the question above about “when will it be available everywhere”, well, the answer is basically it is *already* available everywhere, but older browsers will have to download a JS implementation of it, whereas the newer ones do it natively. If you can afford the performance hit (of node selection across the entire document tree in JS) in the older browsers, you’re good.

    Pity it’s CSS selectors, though, instead of XPath. You could always implement the CSS selectors on top of an XPath processor–good luck going the other way.

    Oh, well.

  • Henrik Lindqvist

    Howdays many selector implementations exists and Dean Edwards may not be the best choice regarding speed. Check out our implementaion Selector.js, and Slickspeed benchmarks.