Alexander Dickson

Selecting elements with JavaScript

If you’re like me, you started out writing JavaScript with the help of jQuery (well, I fumbled around first before someone showed me jQuery). It was and is a lot easier to have jQuery over to do the leg work of abstracting away browser compatibility issues and (arguably) smoothing over the DOM API.

Sometime in the last few years, I decided to drop my shackling to jQuery and figure out the DOM/JavaScript for myself. I’d like to share one of the first things I decided to learn: selecting elements. Let’s have a look at how selectors work in jQuery. The jQuery library decoupled its selector engine a while back into a separate project called Sizzle. Let’s talk about Sizzle then, shall we?

Sizzle takes an input string and returns a reference to the elements it matches. How does it do that? Well, it leverages the native browser API as much as it can and delegates to string parsing when the environment can’t do the leg work for it.

The first thing Sizzle does is determine if the selector string is an id, class or tagName selector. Why? The super dooper native selector parser actually handles these cases much slower than the other older methods used to select these elements. Those methods are getElementById(), getElementsByClassName() and getElementsByTagName(), all available on the document object, among others. Because Sizzle supports IE6, it can’t always delegate to getElementsByClassName(), as it didn’t exist.

Sizzle("#primary")[0] === document.getElementById("primary"); // true.

Sizzle(".external")[0] === document.getElementsByClassName("external")[0]; // true, unless IE6 (see below).

Sizzle("a")[0] === document.getElementsByTagName("a")[0]; // true.

In the case of IE6, Sizzle needs to re-implement getElementsByClassName(). Here is a stand alone implementation you may like to use…

var getElementsByClassName = function(className, node) {
    node || (node = document);

    var descendants = node.getElementsByTagName("*"),
        descendantsLength = descendents.length;
        i = 0,
        matched = [];

    for (; i < descendentsLength; i++) {
        if ((" " + descendants[i].className + " ").indexOf(" " + className + " ") > -1) {
            matched.push(descendants[i]);
        }
    }

    return matched.length ? matched : null;
}

Why didn’t we just shim Element.prototype.getElementsByClassName()? Well, that’s a post for another time (seriously though, see Kangax’s post, main reason for me is I can’t return a live NodeList).

This may be the start of some annoyances when you drop your beloved; without jQuery, working with the DOM API in a cross browser compatible way becomes (usually) verbose quickly.

Now that we’ve handled those three cases, Sizzle will chunkify the selector string and attempt to use querySelectorAll() (if it exists in the implementation), the native method for using a valid CSS selector string to match elements within the DOM. Sizzle will execute querySelectorAll() with the selector string if it determines it can use it to match the chunk. There are many cases where it can not, such as the Sizzle proprietary :contains() selector.

So, when you’re battling on without jQuery and know your browser supports querySelectorAll() (or its one-match-pony cousin querySelector()), you can use it to select elements in a jQueryish way. If you simply want to select an element by its id attribute, class attribute or simply by name, you may be better off using the old methods described above, simply for browser compatibility and the bonus speed benefits.


Want to discuss this post? Just mention me @alexdickson.