Last Updated: April 13, 2020
·
15.26K
· argyleink

DO NOT $('#foo a'); DO $('#foo').find('a');

Don't
$('#foo a');

Do
$('#foo').find('a');

Imagine you are asked to find every student in a highschool, then told to find the students inside classroom #123. Doesn't feel efficient does it?

Imagine now you are asked to find room #123, then told to find every student.

This is exactly how it is for jQuery and the DOM. Selecting by ID is super quick, and if you use an id to find an element first, then the next part of the search only has to happen on it's children, instead of the whole page!

This will increase your app 20 fold. Do it.

35 Responses
Add your response

@nvartolomei So, if I understood: $('div a') <- OK $('div').find('a') <- BAD $('#foo a') <- BAD $('#foo').find('a') <- OK

over 1 year ago ·

I was under the impression, somehow, that whilst the selector engine does work right to left that it does optimise easy stuff like ID selection?

That said, for less generic cases, minimising the search space is definitely a good idea. You can also compact it a little by doing jQuery('a', '#foo')

over 1 year ago ·

Maybe you could link to an actual http://jsperf.com/ link.

And maybe you'll see that the performance gain is worth only when you are doing billions of jQuery selects in a for loop.

So, if you're not doing it in a for loop then your wasting your time trying optimizing something that only happens once.

over 1 year ago ·

@vvo I think this approach could go not only in the optimization sense, but more in a coding ethics and good practices mode.

That said, I do agree, the narrowing the selections in the "search selector" is a great approach.

over 1 year ago ·

Looks like it doesn't matter, at least for new browsers.

http://jsperf.com/wtfasdasdasd/2

over 1 year ago ·

burntime, those results are crazy. looks like i can write a selector any way i want to now. is it jquery fixing and optimizing my bad selector or is the browser?

over 1 year ago ·

@burntime: I think your test should prepare a more realistic, i.e. much bigger DOM. If the original protip was right and $('#foo a') had a complexity of O(n) and the faster one O(1) than the difference would only show by having a big enough n.

over 1 year ago ·

I don't think this is true. jQuery uses Sizzle, and Sizzle is smart enough to lookup nested elements. Seeing how the most left is #foo, it will first grab foo, and then look for 'a'. This might be different for other browsers, but I'm pretty sure Sizzle is smart enough.

over 1 year ago ·

AFAIK $("a", "#foo") == $("#foo").find("a")
"$(subject, context):: Find 'subject' in 'context'"

over 1 year ago ·

this tip in vanilla js = document.getElementById('foo').querySelector('a'). just throwing that in there so the practice can extend into non-jquery js apps.

over 1 year ago ·

It is important to know that jQuery Selectors are handled left to right! The whole "protip" suffers from the misunderstanding that jQuery would work like CSS (right to left).

That's why the analogy with the students in room is wrong.

Doing $('a #foo'); would be searching all students and then find the students from room 123. And it would be very inefficient in the DOM.

Doing $('#foo a'); is similar to $('#foo').find('a'); and only has the performance impact od deconstructing the String "#foo a" into "#foo" and "a" (and function calls. The DOM traversion should be nearly the same.

// Update 2012-12-03:
As @mlb has said, in general, Sizzle selectors are handled right-to-left. Exception: when there is an ID as the first element/operand – which is the subject of this discussion – then jQuery/Jizzle optimizes it by first selecting the ID element and then searching inside it. (At least that is what I have found.)

over 1 year ago ·

Why should I have to compensate for a bad implementation? If $('#foo').find('a') is faster than it should be what $('#foo a') translates to inside of jquery. I shouldn't have to worry about it.

over 1 year ago ·

Don't do that "do", do that as @cyrusboadway did.

over 1 year ago ·

But what if I have a more complex query: like $("#foo div.something a"), then do I use $("#foo").find("div.something").find("a")? Kind of long ...

over 1 year ago ·

I created a test at jsperf a whole back that covered this in a little more detail: http://jsperf.com/jquery-context-or-no-context

over 1 year ago ·

jQuery find() convention has bad performance

For DOM queries use Cascading $('#foo a') or parent > child $('#foo > a').

// bad
$('#foo', 'a').hide();

// bad
$('#foo').find('a').hide();

// good
$('#foo a').hide();

// good
$('#foo > a').hide();
over 1 year ago ·

@thomaspuppe Sizzle selectors are handled right to left. Your whole comment suffers from this wrong belief.

over 1 year ago ·

@thomaspuppe This "protip" showed up in my e-mail box and I read it with horror. Thank God your correction is the top comment.

over 1 year ago ·

@mlb You are absolutely right! Sizzle selectors are handled right-to-left.

Is this something that has changed recently? I always believed jQuery works left-to-right, and noone ever disagreed. Maybe it is because I am used to put IDs as the first operand, and have this in examples and tests as well.?

EXCEPTION: when there is an ID as the first element/operand – which is the subject of this discussion – then jQuery/Jizzle optimizes it by first selecting the ID element and then searching inside it. (At least that is what I have found.) This behaviour is like working left-to-right .

Sizzle is highly optimized and I guess they have different behaviour for different selectors and also take browser features into account.

In general, I can only repeat the advice some people already gave: Testing a certain solution onj jsperf (or similar) is always a good idea. E.g.: http://jsperf.com/specific-left-or-right

over 1 year ago ·

@thomaspuppe By the way I usually recommend to jQuery users to create a helper like :

function $$(id){return $(document.getElementById(id))}

Even if '#id' selectors are optimized, they remain really slow compared to the helper,

over 1 year ago ·

Just wanted to note that this is only correct when searching by ID.

Searching by tag name yields different results in modern browsers where $('div').find('p')</code> is slower than $('div p')</code> because the latter uses querySelector().

http://jsperf.com/find-vs-sizzle-jf

over 1 year ago ·
over 1 year ago ·

I personally would very much prefer to use plain old JavaScript for selecting child elements 'p' of an element #id. I would also recommend doing it this way as well, especially after having added a couple more tests to the jsperf and seeing just how extremely much faster it is to do what I told my friend I'd do instead by default. http://jsperf.com/wtfasdasdasd/5

var p = document.getElementById('id').getElementsByTagName('p');
$(p);

Plus, you can set it to a variable which can be passed to jQuery for selecting it and using it's context.

over 1 year ago ·

Obvious but I loved the metaphor with classroom! :)

over 1 year ago ·

thanks for explaining what is happening here. I just used find() for the first time today and didn't realized how much more efficient it is.

over 1 year ago ·

Just wrote what I think is a more comprehensive test scenario:

Note that the performance varies between the nested and flat DOMs.

over 1 year ago ·

@burntime oh, I did not know that app. Thank you very much!

over 1 year ago ·

The difference in actual runtime performance here is so tiny, I doubt if it matters in any real-world use case (who on earth matches millions of selectors, not to mention in a loop over time?).

Just use what's more readable and makes sense in your context. If you nailed the big stuff, tweaked the medium issues and ran out of micro-optimizations, come back to this tip.

over 1 year ago ·

Exactly. I am wondering what kind of understanding author has. @argyleink.: Get some reading before you make so called PRO-TIP.

over 1 year ago ·

So I guess we can not call it PROTIP

over 1 year ago ·

I've noticed that about an alarmingly large number of "pro-tips" on this site. Most of the ones I read in a sitting are either incomplete or flat out wrong. Too bad this site is more about vanity and less about actual knowledge (i.e. StackOverflow) or else there might be a better system for squashing anti-knowledge.

over 1 year ago ·

There's tons of optimizations in JQuery and sizzle and then since they use DOM too, there's another set of optimizations are done. What I'm saying is that no matter how much you know about JQuery, Sizzle(, or even a certain browser internals and how it deals) it wouldn't be easy to just predict what operations will faster. There's too many optimizations that may or may not happen. I suggest you all read this two:

http://mrale.ph/blog/2013/04/29/performance-tuning-as-weather-forecast.html
http://mrale.ph/blog/2012/12/15/microbenchmarks-fairy-tale.html

If you want what is faster, write a real world test case for both cases. At times it may not be even noticeable. And again don't just try to predict stuff like this. If were you I would go with $('#foo a'); because it's easier to read. Later IF I FEEL MY APP IS SLOW , I'll profile it. If $('#foo a'); was the bottleneck, I just replace it with something efficient, but this I make sure I'll measure it correctly.

over 1 year ago ·

I like the analogy, however it's up to jQuery to interpret the query. It could be that both queries are the same, ie, jQuery could split the query "#foo a" to $("#foo").find("a") to optimize performance. However good or bad can't be determined just by basing on the analogy, it must be benchmarked and looked at the source.

over 1 year ago ·

similarly, do: $(document).on('click','.fancybutton', function(ev) { /* ... */ });

over 1 year ago ·

It's different things

over 1 year ago ·