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 »
10,638 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 »
160 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 »
10,638 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 »
10,638 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 »
10,638 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 »
10,638 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 »
10,638 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.



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
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 3, 2012 at 10:49 PM
How I Got Node.js Running On A Linux Micro Instance Using Amazon EC2
Wow this was really helpful! Only thing I would add is you need to update your .bash_profile after you edit the secure_path. This is what I did: $ . ~/.bash_profile Otherwise, NPM won't be found. ... read »
Feb 3, 2012 at 10:14 PM
Pushing Base64-Encoded Images Over HTML5 WebSockets With Pusher And ColdFusion
@Ben, Just wanted to let you know that pusher are soon to start limiting sizes on messages. This was the detail that came through in the Feb dispatch: "However, we will soon be limiting the s ... read »
Feb 3, 2012 at 5:05 PM
Regular Expressions Make CSV Parsing In ColdFusion So Much Easier (And Faster)
I tried using your RegEx in my C# program, but it was matching an extra empty-string at the end and so I would end up with an extra field that doesn't exist, so I changed it to this: (^|,)("(?: ... read »
Feb 3, 2012 at 3:47 PM
ColdFusion Supports HTTP Verbs PUT And DELETE (As Well As GET And POST)
Josh Cyr posted this on Twitter just a little bit ago. Thought it was appropriate. http://stackoverflow.com/questions/1619152/how-to-create-rest-urls-without-verbs/1619677#1619677 ... read »
Feb 3, 2012 at 2:28 PM
Changing The Execution Context Of Your Self-Executing Function Blocks In JavaScript
@Michael, You definitely make a good point (and extra points for quoting movies - I love movies). When you use a return() statement to define the object's public API, it does provide a consistent a ... read »
Feb 3, 2012 at 2:04 PM
Changing The Execution Context Of Your Self-Executing Function Blocks In JavaScript
To quote Jurassic Park: "Just because you can doesn't mean you should". I completely, utterly disagree with the thought that this is more readable. Consider the current module pattern: if ... read »
Feb 3, 2012 at 1:10 PM
REST API Design Rulebook By Mark Masse
@Jordan, Yeah, WRML was created by Mark Masse (author of the book). I also found it to be a bit convoluted. I suppose it is intended to allow the Client to be able to programmaticaly respond to cha ... read »
Feb 3, 2012 at 1:08 PM
ColdFusion Supports HTTP Verbs PUT And DELETE (As Well As GET And POST)
@Jason, To be honest, I don't have good answers for that kinds of stuff. And, to the point, that is specifically why I *really* liked the REST API Design Rulebook by Mark Masse - he just cuts throu ... read »