Improve your jQuery Selectors: 5 Quick Tips

jQuery is a wonderful way to make Javascript usable for everyone. For those not familiar with jQuery, I suggest you load up jquery.com and have a read through. I’ve and few other authors have also written a few jQuery articles on this very site in the past which you might have read. Here are a few that might interest you.
Build Fancy Tabs in JQuery that Fade Text
jQuery – Javascript Made Easy
Tips for jQuery Live Events
jQuery Plugin Tutorial

This post is about going through previously written code and looking at ways we can improve it, both the readability and the efficiency. Mostly these are little snippets of jQuery that perhaps don’t get noticed during a browse through the documents or are rarely useful. So, I’ll shut up and we’ll get started!

1. Don’t Select by Class if Possible.

Native Javascript contains two selectors: getElementById and getElementsbyTagName. You will notice there is no function to select elements by class. jQuery and most libraries do offer this function, and it’s not hard to write your own function to do it. However, due to it not existing natively, any custom function will be slower than using the two native functions above. So if you can avoid it, select elements by ID or by Tag Name over class where ever possible. Sometimes it is unavoidable of course, but if you can avoid it, do.

2. Save Selectors or chain ‘em up

See what’s wrong with this code?

$("div#elem").hide();
$("div#elem").css("width","200");
$("div#elem").show();

Nothing is exactly wrong. However a lot could be improved. Each time $(“div#elem”) appears, jQuery does a search through the DOM for it. In that snippet of code, jQuery has to search three times for the element. Why make it do all the work? Either chain the functions like so:

$("div#elem").hide().css("width","200").show();

Or save the element (also known as caching):

var myElement = $("div#elem");
myElement.hide();
myElement.css("width","200");
myElement.show();

Any element that you know you’re going to be referencing loads, save. However if you’re just using it in one block of code, I’d use chaining.

Caching also applies to the “this” keyword. Consider this loop:

$("div").each(function(i) {
$(this).addClass("myClass");
if($(this).hasClass("newClass") {
$(this).addClass("anotherClass");
}
});

While this might look all cosy, look how many times jQuery has to select the current element using $(this). This loop is much better:

$("div").each(function(i) {
var $this = $(this);
$this.addClass("myClass");
if($this.hasClass("newClass") {
$this.addClass("anotherClass");
}
});

When saving $(this) to a variable, I always name that variable $this, adding the dollar symbol at the beginning. This is because I can’t call a variable “this” as it’s a reserved word.

3. Contextual Information

If you can provide extra information about the location of an element in the DOM, do. For example:

$(".myElem")

Will work fine, however if you can do this:

$("div.myElem")

This tells jQuery that the item with the class “myElem” must be within a div. So jQuery can use getElementsByTagName first to narrow down the search.

If you’ve already cached an element (see Tip 2) then you can actually add it as a second parameter when selecting another element:

$(".myElem", elementYouCachedInAVariable);

This tells jQuery to search for items with a class of “myElem” from within the cached element, again shortening the amount of work jQuery has to do.

4. Avoid Extra Traversing

The most common example of this is as follows:

$("elem").find("p").hide().parent.find("h2").hide();

What happens here is we find paragraphs within an element, hide it, then select those paragraph’s parent again, then find any h2 elements within that element, then hide it. What’s wrong? We start at the parent level, then find paragraphs within it, but then go back up to parent level to find the h2s within the parent. Why not use the siblings function?

$("elem").find("p).hide().siblings("h2").hide()

But this can be even further improved, as the find() function can take multiple selectors:

$("elem").find("p, h2").hide();

Take a look at the original code and what we have now. Much nicer!

5. Make use of andSelf()

Rarely used as much as it should, andSelf() is useful for chaining the previous selection. For example, take code such as this:

$("div").find("p").addClass("myClass").parent().addClass("myClass");

That would seem the only logical way to do things. But no! Check this out:

$("div").find("p").andSelf().addClass("myClass");

Which again, saves us repeating the addClass() function and generally is nicer to look at.

If you have any questions, please leave a comment or grab me on Twitter: @Jack_Franklin.

17 Comments on "Improve your jQuery Selectors: 5 Quick Tips"

  1. Mike Hopley says:

    Great article, Jack.

    Under your “Contextual Information” section, it’s interesting to note that this performance behaviour is the exact opposite of CSS:

    – In jQuery, $(“div.someClass”) matches faster than $(“.someClass”)
    – In CSS, .someClass matches faster than div.someClass

    The reason for this counter-intuitive performance behaviour is that CSS matches right-to-left: browsers check the rightmost (simple) selector first, and then traverse up the DOM to match selectors on the left.

    Javascript DOM traversal works in the opposite (more intuitive) direction: first you have a getElementsByTagName() loop, which dramatically narrows down the result set, and then you check those items for a class name.

    • Eric Hynds says:

      “querySelector” implementations in modern browsers use the available CSS engine, so selecting with querySelector/querySelectorAll works from right to left as well. Sizzle, the selector engine built into jQuery, also works from right to left.

      • Mike Hopley says:

        Oh, that’s interesting! It seems Sizzle doesn’t work at all the way I imagined it would.

        Does that mean that more highly specified selectors in jQuery are actually slower to match? For example, is $(“div.myClass”) slower than $(“.myClass”)? And is $(“#myId .myClass”) slower than $(“.myClass”)?

        In other words, if we want faster selector matching in Sizzle, we should be following the same rules as for efficient CSS selectors ( http://code.google.com/speed/page-speed/docs/rendering.html#UseEfficientCSSSelectors ).

      • @Mike

        Your expectations are actually correct regarding Sizzle.find
        the default setting for Sizzle is to use ID specificity first
        after which it goes from left to right

        see http://wiki.github.com/jeresig/sizzle/

      • Actually, it’s my understanding that sizzle goes both ways. Take this as an example:

        jQuery(“div.header ul”)

        It will first look for all “ul” nodes.
        Then within that list, it will make sure one of its parents is a “div” node
        Lastly it will narrow those nodes down to the class (“.header”)

        Thus, it is faster to do this:
        jQuery(‘ul’,'div.header’)

        Which is equivalent to this:
        jQuery(‘div.header’).find(‘ul’)

        ——
        Or the other way around:
        jQuery(“div.tasks p.urgent”)
        1. looks for all ‘p’
        2. narrows all p’s to .urgent
        3. makes sure one of its parents is a div node
        4. makes sure one of its div parents has .tasks class

        Better:
        jQuery(‘p.urgent’,'div.tasks’)
        same as:
        jQuery(‘div.tasks’).find(‘p.urgent’)

        Let me know if I’m wrong :)

      • David O'Trakoun says:

        I’d have to look at the source again but I think the discrepancy here is between sizzle and jquey. Jquery’s find is a wrapper for sizzle, whereas jquery(“selector selector”) will iteratively call .find() — sizzle — in the order of jquery’s logic

  2. Rilwis says:

    Nice tips. I haven’t used andSelf() before. Seems to be a good function. Thanks for sharing.

  3. Jennifer R says:

    It’s nice tutorial, thanks you for sharing me how to use cache with Jquery :)

  4. Nice write up – but I have a comment on point number 3 – I prefer to use the find method instead of using the context when searching cached elements. I think that it’s much easier to read (plus under the hood of jQuery, exactly the same thing is happening!)

    So instead of this:
    $(“.myElem”, elementYouCachedInAVariable);

    I use this:
    elementYouCachedInAVariable.find(‘.myElem’);

  5. Josh Powell says:

    Good article, some comments:

    1) You are correct that in older browsers getElementById and getElementsByTagName are the only selectors, but newer browsers have querySelector, querySelectorAll and getElementByClassName. So, if you are targetting older browsers like ie6, what you are saying still makes sense but this won’t always be true.

    2) $(‘div#elem’) is a selector you should never, ever use. You have an id in there, so $(‘#elem’) is faster because it shortcuts to selecting by id, putting div in there makes it check for divs as well. Id’s on a page are required to be unique, so adding div to it doesn’t make it more specific

    3) Unfortunately, it’s not always this clearcut and simple. A lot of selector performance depends on the structure of your html code. Selectors where performance is an issue will need to be tested for speed. As to the left to right or right to left, I believe this refers to descendant selectors:
    $(‘tr td’) looks for all tds on a page, and then traverses upwards looking for parent tr, and $(‘tr.foo td’) gets all tds on a page, and then traverses upwards looking for trs with a class of foo. That’s not that much different, a quicker selector might be $(‘tr td.foo’) as it would only select all tds with a class of foo and traverse upwards from there. That is speculation I haven’t tested however, beware of premature and unnecessary optimization with selectors and traversals, I’ve been using jQuery daily for about 3 years now and have never had a selector be the source of slowness in my code. You #2, on caching is more important.

    4) Another trick is to stick it all in the selector:
    $(‘elem p, elem h2′).hide()
    or to cache
    $(‘p, h2′, ‘elem’).hide()

    5) $(‘div, div p’).addClass(‘myClass’)

    6) My current favorite trick to reduce if/else, here is a simplified example

    $(‘div’).each(function() {
    if ($(this).hasClass(‘foo’)) {
    // do something
    }
    });

    can be reduced to

    $(‘div.foo’).each(function () {
    // do something
    });

    • Great points Josh. I was going to mention getElementsByClassName, I use that selector often. I tend to have a lot of class names due to the fact that I am obsessed with code reuse.

      I also like your conditional code that has been shortened. I might even take it a step further and put it all on one line.

  6. Valuable tips. Specially about contextual information.

  7. Ain says:

    The author is well wrong in point one about the class selectors. Select by class is optimised on modern platforms, WebKit added support for getElementsByClassName back in 2007: http://tekkie.flashbit.net/browsers/webkit-adds-getelementsbyclassname

    • Unfortunately the latest versions of IE (6, 7 & 8) do not support getElementsByClassName – therefore I believe the author is spot on. Of course – if you only develop for modern browsers than this is a different deal – but – I guess that every web app out there will still get a lot of hits from IE – and you need to care about them too.

  8. Good article, I have only two comments:

    1) The Avoid Extra Traversing second code block is missing one double quotation mark

    2) On the var myElement = $(“div#elem”); piece of code, you didn’t use the $varName, meaning preceding a jQuery object variable with the dollar sign, but you didn’t below, I use this all the time and it’s a readability candy!

Got something to say? Go for it!