Your jQuery Selector Context Can Be A jQuery Object

Posted March 15, 2010 at 9:04 AM by Ben Nadel

Tags: Javascript / DHTML

This is just a quick post to clear up any confusion over what kind of objects can be used as a context when performing a jQuery selector execution. Sometimes, when reviewing jQuery code, I see people make data type conversions in their context usage:

  • $( "div", myStuff[ 0 ] )

Here, you can see that the programmer is converting their "myStuff" jQuery collection into a single DOM node to be used as a selection context. This might make sense if the myStuff collection contained nodes within which you did not want to search; but more often than not, this data type conversion is entirely arbitrary. And, more than that, it is unnecessary, and inefficient.

If you look at the documentation for the $() method, you will see that the context argument can be a DOM element, a document, or a jQuery object. And, if you check the actual source code, you will see that the use of the context:

  • $( selector, context )

... is actually turned into a find() method internally:

  • $( context ).find( selector )

To optimize this execution, jQuery actually checks to see if the given context is a jQuery object; if it is, it performs the find() method call directly on the given context. If the given context is a DOM element, however, it first converts it to a jQuery object and then executes the find() method. So, actually, if you extract a DOM node as the context from a given jQuery object, you're slowing down execution, forcing jQuery to re-package the DOM node as a jQuery object.

When using a context, I personally find the find() method to be easier to read; but, if you're going to use the context argument in the $() method, just remember that it can be a jQuery object; there's no need to pass in a DOM element if you don't already have one.



Reader Comments

Mar 15, 2010 at 10:06 AM // reply »
2 Comments

Hi Ben,
But Brandon Aaron says it the other way. Check this:
http://brandonaaron.net/blog/2009/06/24/understanding-the-context-in-jquery

I've tried his samples and I've tried the same in my project too. I found that selectors perform better when context is DOM node rather than jQuery object. Well, I'm not sure if jQuery 1.4.2 has any changes in context execution. I'm using 1.3.2 in my project.


Mar 15, 2010 at 10:26 AM // reply »
11,246 Comments

@Krishna,

I believe what Brandon is referring to is the actual "context" attribute of the jQuery collection. This is somewhat of a "behind the scenes" property.

What I'm referring to is the context used in the $() method. Brandon points out that this does become $().find() under the hood; all he's saying is that the type of context passed has a different effect on the context property. As far as why the difference, I'm not really sure.

As far as performance is concerned, yes, it appears there are differences in 1.3 and 1.4 versions of the library. 1.4 seems to take more variety into account, probably optimizing for more use cases.

In jQuery 1.4, it definitely tries to use the existing jQuery object before it checks for a raw DOM element (which it then converts to a jQuery object).

In jQuery 1.3, the init() method appears to be much less optimized, simply calling:

jQuery( context ).find( selector );

... in this case, you might be right about using a raw DOM element as slightly faster as this will be the more slightly optimized case.

In any case, however, you can use either a jQuery collection or a DOM element.


Mar 15, 2010 at 10:59 AM // reply »
171 Comments

If you *know* you're dealing with a jQuery object, always do:

context.find(selector)

over:

$(context).find(selector)
- or -
$(selector, context)

IMO, passing in a jQuery object as the context is only useful if you've written code where the context could be either a DOM node or a jQuery object, because the function/method is used multiple ways. Whenever possible, I like my code explicit in what the context type because there is some overhead in jQuery when it comes to initializing new jQuery objects. If you don't have to initialize a new object, don't do it.


Mar 15, 2010 at 12:17 PM // reply »
11,246 Comments

@Dan,

I agree regarding context.find(). To me, that's the most readable way to do it; and, since the majority of times (for me), my context is already a jQuery collection, it's what makes the most sense from performance.

jQuery 1.4.2 seems to use this approach when possible over the raw DOM element - but you still get the extra method call involved.


Mar 15, 2010 at 12:48 PM // reply »
8 Comments

There is one case in which you need a DOM node as the "context" argument: when you're using it for the .live() method. Otherwise, you're right.


Mar 15, 2010 at 12:50 PM // reply »
11,246 Comments

@Karl,

Is this for when it uses delegation?


Mar 15, 2010 at 1:03 PM // reply »
8 Comments

@Ben,

Yeah. By default, .live() binds events to document and uses event delegation to see if $(event.target).closest(selector) exists before executing your function.

As of jQuery 1.4, you can specify a DOM element to bind to instead of document, which limits the possible elements that trigger the event.

More info here: http://api.jquery.com/live/#event-context

(hope that makes sense. I'm typing quickly while at work thinking about a bunch of other stuff. :) )


Mar 15, 2010 at 1:09 PM // reply »
11,246 Comments

@Karl,

Ah, gotcha; yes you are making sense :)


Mar 15, 2010 at 3:24 PM // reply »
10 Comments

My feeling is that using a jQuery object as the context argument only hinders readability, and the performance will be slightly worse (however negligible) because of that extra internal conversion. I sometimes see $("expr", cachedjQueryObject) being interpreted as $("expr").andSelf(), rather than $(cachedjQueryObject).find("expr"). The latter issues a command and is more procedural/self-documenting.

I tend to use the context parameter only when inside an anonymous callback function and the context will be "this". IMO, $("span", this) is just as readable as $(this).find(), but that could just be my syntax highlighting :)


Sep 12, 2010 at 2:06 AM // reply »
2 Comments

My understanding is that the context parameter behaviour has changed in jQuery 1.4, and it was not good practice prior to pass the jQuery object as a context. Cody Lindley notes at the beginning of chapter 9 of 'jQuery enlightenment' (for 1.3.2) about passing context -

'For a performance gain to be made, you need to pass an actual DOM reference as the second parameter. Passing the jQuery object itself - e.g. $('#context') - still requires a search of the entire document for the reference. Searching the entire document completely negates the point of passing a context.'

So until 1.4, it was necessary (unless Cody was wrong).

Cheers,

Colin


Sep 12, 2010 at 6:55 AM // reply »
11,246 Comments

@Eric,

I tend the prefer the $( ... ).find() approach; but that might just be because I typically have a jQuery collection to work with at the time. As you are saying though, when in a callback, I will also use the "this" reference as the context node and it works quite nicely.

@Colin,

I'll have to take a look at the jQuery source code for 1.4.2 to be sure; I'll get back to you on that matter.

As far as what Cody Lindley was saying, I think perhaps what he meant was that you simply don't want to be performing jQuery-look-ups as *part* of the context definition. Meaning, if you have a jQuery collection already (and therefore have DOM nodes within it), then you can use it; but, don't use a jQuery collection if you have to look up the nodes to define the context meaning.

I could be misunderstanding, but I think about it like this - imagine I have some callback that passes in a jQuery collection:

doSomething( myCollection ){
return( $( ".target", myCollection ) );
}

... here, I am using the given jQuery collection (myCollection) as the context. I believe this is ok since the jQuery collection has already searched the DOM and collected the given DOM nodes.

However, I think what Cody's saying is that you want to avoid things like this:

doSomething(){
return( $( ".target", $("#parent") );
}

... since jQuery had to perform two searches - one for Parent and one for Target. If you have that kind of a setup, then you can probably just roll it into one search:

$( "#parent .target" );

... this way, Sizzle can really optimize the lookup.


Sep 13, 2010 at 7:40 AM // reply »
2 Comments

Not sure about that - at other points in his book he explicitly uses the following convention:

$('a', $('#context')[0])

Not saying for sure this is correct, but certainly the code you are seeing with this syntax could very well be the result of expert advice - I know I used that convention specifically after reading this book.

Cheers,

Colin


Sep 13, 2010 at 8:57 PM // reply »
11,246 Comments

@Colin,

I just took a look in the jQuery 1.4.2 development (non-minified) version and the init() method which handles the jQuery collection creation looks like this (minus a LOT of code) ~Line 159:

// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
. . . return (context || rootjQuery).find( selector );

// HANDLE: $(expr, context)
// (which is just equivalent to:
// $(context).find(expr)
} else {
. . . return jQuery( context ).find( selector ); }

It looks like the jQuery library still handles both versions. In the end, if the context object is not a jQuery object, you can see that internally, the code is still converting the context DOM node into a jQuery object.

So, whether you pass in a DOM node or a jQuery collection object as context, you still end up with a jQuery collection. It looks like both ways are equally performant - it just depends on what you are going to do with the context object after you pass it in as a context. Meaning, if you are going to use it externally as a jQuery object, you might as well convert it externally.


Mar 1, 2012 at 9:48 AM // reply »
1 Comments

You know a way to pass many contexts?

  • $('.test', $('#container-1, #container-2') ).show();

Tks



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 24, 2013 at 5:39 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Adam Oops! My mistake! I hadn't gotten that far in my testing - I'm still baby stepping my way through the process. ... read »
May 24, 2013 at 5:13 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
Hi Jason, Thanks for checking up on that, but I still stand firm on my position. :) There are actually two listLast()'s in use, and you're right that the one using a space as a delimiter is fine. ... read »
May 24, 2013 at 4:45 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Ben I have been lurking your site for quite some time, and haven't stepped up to comment until today. Thanks for all the great info - keep it up! @Adam I believe you are mistaken... as the commen ... read »
May 24, 2013 at 11:21 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, Ha ha, let's us never speak of justifying "##" notation again :P ... read »
May 24, 2013 at 11:18 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Ah, so it was indeed how I vaguely remembered it to be: A direct assignment value = users.id[ i ] causes value to retain the sticky datatype of the query column. Although unnecessary in ... read »
May 24, 2013 at 9:11 AM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Brandon, Hi, No, I haven't been able to do that. I have just kept it as it is. ... read »
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools