The Real Scoop on jQuery .find() Performance
January 8th, 2013
.find() seems to get a bad rap. Some people state that selectors are WAY better for performance, some people say that .find() is. Well, actually both can be true. Not only depending on what amount of elements you are searching, but HOW you are selecting or finding them as well. Take this example for instance:
Please take note, all percentages of performance given are estimated based on testing via jsPerf and with the latest browsers at the time of post.
$("#a .b") $("#a").find(".b");
What’s faster? Yep, you guessed it, the .find() wins this battle. How much? By about 25-75% faster depending on the browser/operating system. How about this one?
$(".a h1, .a h2, .a h3"); $(".a").find("h1, h2, h3");
What’s faster? Well, you probably guessed it as well… why else would I offer two examples if one of them wasn’t faster as a selector? The selector wins this match by about 25%. It has to do with this case’s unique assortment of elements. First it’s trying to grab a class, then an element inside it, both of which are traditionally slower than using ids. It just so happens, .find() doesn’t do a good job with this assortment. Re-write it as $(“#a”).find(“.b, .c, .d”); and it does absolutely fine once again. There’s quite a few of these quirks with .find().
So what does that mean? Do we have to test every piece of code to see which is faster? No. There’s a lesser known trick with .find() that allows ALL queries, no matter how big or small, or how complex or simple, no matter which type of element/id/class. It’s all about in how you write it. Let’s take a look at this case:
var $a = $('#a'); $a.find('.b');
As it turns out, it’s around 50% faster to first store (or cache) the parent element as a variable when using .find(). Most likely, you will be able to implement this provided your parent element isn’t always changing ids or something like that, but even if it was (for whatever reason), just re-store the element into the variable. But why stop there?
There’s also selecting items via “context”, like so:
It’s not very fast, but why not cache it:
var $a = $('#a'); $('.b', $a)
This “cached context” method is fairly fast, almost on par with a cached .find(). There’s also this:
Why not drop the parent altogether? Because, as it turns out, this is around 50% faster than the previous example. This method has been dubbed the “speed find”. This method has a huge downside though, since it’s not looking in any specific container/parent it’s going to grab the “b” class wherever it can find it… which is not realistic in most implementations. Not only that, but if you want to use it for any practical purpose, like say, performing a .addClass(), or a .css(), you need to wrap it like so:
That kind of defeats the purpose now, doesn’t it? It also makes it slower than the previous iteration of a cached .find(). Here comes a curve ball!
.children() is actually pretty fast too. But it has a lot of quirks, kind of like .find(). Using .children() is around 50% faster than a cached .find() when selecting something like this:
var $a = $('#a'); $a.children("h1");
However, if we do the same “#a .b” combo, it becomes much slower than a .find(). This is because, finding elements or ids is much faster than classes, and it just so happens .children() does it faster than the rest of the methods.
So who is the “practical” winner here?
var $a = $('#a'); $a.find('.b');