views:

104

answers:

4

Just curious if there is an established "best way" to target child elements inside of a parent element.

For example, if I want to create a click event on the children of a parent element, which one should be preferred?

a) give the parent element and id and do:

$('ul#parent li').click(function() {});

b) or, instead, give each of the children a class name and target them directly:

$('li.child').click(function() {});

I'm asking because I'm trying to squeeze out every bit of performance I can get in a somewhat large application. Logic would dictate that targeting an id is faster than targeting a classname, but does the parent > child structure negate that advantage and justify targeting by classname instead?

Thanks for any insight.

+7  A: 

This would be the fastest option:

$('ul#parent li').click(function() {});

Always descend from an ID selector if possible.

However if you have a lot of <li> elements, it's cheaper to use .delegate(), like this:

$('#parent').delegate('li', 'click', function() { });

With .delegate() you're attaching one event handler instead of n (number of <li>) event handlers, one for each <li>. It works off listening for the click to bubble up to the <ul>, if startup time is killing you, this is a much better option. It's a tradeoff of startup time binding lots of handlers vs. the bubble cost (which is a very, very, very small cost). If you have lots of elements, this is almost always a better overall option.

Nick Craver
The `ul` preceding `#parent` is entirely redundant.
J-P
@Nick - Thank you! Perfect explanation.
bobsoap
@J-P - *Most* of the time you're correct, which is why I removed it in *my* example, but the question was which of the *original* ones were faster...it's a multiple choice question :)
Nick Craver
Sorry, should've left my comment on the OP.
J-P
@J-P - you are absolutely correct about that. I was trying to illustrate the HTML structure, although it would really be obvious that the parent is an ul. I actually have nested `<div>`s there anyway :)
bobsoap
@Nick - Would you consider it a best practice to *not* place a tag name before an ID? I've always thought so since my understanding is that doing so impacts performance negatively, but I've come across others on SO who vehemently disagree. What's your take?
patrick dw
@patrick - See my response to J-P in comments :) It *does* impact performance negatively, it depends if the preceding tag is **needed** for the correct level of specificity. For example: using the same script file in multiple pages, `div#message` you want to fade in, it has some server-side message there...but maybe a `textarea#message` for a form has a completely different meaning. The better route would be to fix the IDs, but if that's not an option, the preceding tag is necessary.
Nick Craver
@Nick - Thanks for the response. :o) Yes, I saw your comment above, but thought it was just about the original choices. Anyway, what you said about reusing a script across pages makes sense. I hadn't thought of that. I'd agree that it would seem better to fix the IDs and avoid that situation altogether. Thanks again. :o)
patrick dw
+1  A: 

Selector with id is faster because an id is supposed to be once per element per page rather than a class which can be assigned to multiple elements with same class name.

Sarfraz
Thanks for the input!
bobsoap
@bobsoap: You are welcome
Sarfraz
+1  A: 

Selecting from an ID is faster. If you are doing selections based on that ID frequently it would be faster still to save it to a variable, say $ul_parent, and then base further selections on that variable, like

$ul_parent.children('li');

or

$('>li', $ul_parent); //same as above

This lets you use the fast selector, and cuts out some overhead for later selections. The second form isn't used very often, but the second parameter acts as a context for the selector in the first parameter so the jQuery function doesn't have to search the whole page. It just searches the already created jQ wrapper object.

jasongetsdown
Thanks for this, great call on saving selectors in a variable. I'm trying to make this as fast as possible and I'm sure this tip will go a long way for me.
bobsoap
+1  A: 

While theoretically $('ul#parent li') is faster than $('li.child'), pratically there aren't much differences.

In a microbenchmark (http://jsbin.com/uwela/4) there is 1 <ul id="parent"> with 4096 <li class="child">'s, and 64 <ul> each with 64 <li>'s. On Firefox 3.6 on my machine, all $('ul#parent li'), $('#parent li'), $('li.child'), $('.child') take roughly 210 milliseconds to attach the onclick functions.

But then, the DOM on this test page is very shallow. Test it on your own page to be sure.

KennyTM
Not my downvote...but this isn't a very useful test, you instead need to test the *selector* speed, not the binding speed. You're binding `n` handlers to `n` elements, that's a fixed cost looping through the *same* DOM element array that results from either selector. Now I agree the binding, *when the set is large* is the vast majority of the time spent anyway, but when you're testing, the selector would be where the difference is in this case.
Nick Craver
Interesting test nonetheless. Thanks for the effort.
bobsoap
@Nick: You mean like http://jsbin.com/uwela/5? Assuming `$` is not lazy (i.e. all 4096 entries are returned), the time spent is so short that no reliable comparison can be done.
KennyTM
@KennyTM - Sure it can, like any performance test, run it over and over, e.g. a `for` loop, like this: http://jsfiddle.net/3SMGM/ Open your console in Chrome or Firefox to see results :)
Nick Craver