jQuery makes an excellent choice of framework on which to base your Web application. It eschews a large feature-set in favour of an incredibly powerful selector engine. But if you’re not mindful of jQuery’s core $() function, you face the very high chance of stumbling at the first hurdle. To quote Uncle Ben’s words to Peter Parker, “With great power there must also come — great responsibility!”.

Spiderman In the world of Javascript frameworks, I like to think of jQuery not as a complete support network, but as an ‘able right-hand man’. While it doesn’t provide everything you might need for your hot Web 3.0 application, its strength in DOM manipulation and traversal more than makes up for any shortcomings in esoteric features. Other frameworks allow you to sidestep many tricky problems with handy shortcuts; but with jQuery, you’re left to be the brains while it provides the muscle.

However, despite jQuery’s bulging selector biceps, it needs training and a constant energy supply to perform at its best. This is something that is often overlooked by newcomers —JS pros and beginners alike— and that which is the crux of this entry: context!

But first things first; this entry needs some context. Let’s recap on a couple of salient rules for high-performance Javascript.

Some Rules for Javascript Performance

  1. Frameworks can only be as fast as the native Javascript implementation – native functionality for DOM traversal is pretty much limited to getElementById() and getElementsByTagName().
  2. DOM manipulation is computationally expensive – it is essential to minimise the quantity of the DOM you are manipulating at any one time. It is quicker to work with 5 nodes than it is with 500.

How does this apply to jQuery?

If you’ve got this far, it’s likely you’ve got at least a little experience with jQuery, and you’ll know that the beginning and end of the framework is the core jQuery() or $() function. jQuery()’s power lies in the freedom to use a huge variety of [CSS] selectors to pick out elements in the DOM and provide us with ways to manipulate them. So integral is this function, that its performance can be the most significant bottleneck to your own application code.

Introducing $()

Looking at the jQuery() function signature:

jQuery( expression, [context] )

we see it takes two arguments:

  • expressionAn expression to search with.
  • context (Optional) – A DOM Element, Document or jQuery to use as context

Expression is where the magic of jQuery’s selector engine happens – whether you wish to select elements by class, by nth-child, if it’s visible, a heading element, up to many complex combinations, it’s all possible. Yes, it’s a beautiful thing, but it does come at a price. Just a few of these many selectors exist as native code, and so much of the codebase must implement these selectors in a non-native, albeit succint and clever, manner.

Our first rule of javascript performance states that frameworks can only be as fast as the native functions. Then our fastest selector for a single element must be using getElementById(). It has only to find one unique element in the DOM, and need not continue searching once it has been found. This is followed by getElementsByTagName(), which will pull out a collection of elements based on their tag.

In terms of jQuery selectors, this equates to the following order of speed, with everything else trailing pretty far behind:

  1. $('#elementID') (uses getElementById())
  2. $('div') (uses getElementsByTagName())

Logically then, we should avoid using anything other than these two selectors if we desire optimum performance? Well yes indeed, but surely that negates one of the main reasons for using jQuery in the first place?

So how does this knowledge help us when we need to do something more complicated that requires a slow selector, like pulling out elements by attribute or visibility? How can we improve the speed of these selectors?

Providing the Context

Consider our second rule of javascript performance – always try and limit the area of the DOM we need to work with to reduce the amount of DOM traversed in our code.

Now recall that $() takes in an optional second argument that is a DOM Element, or a jQuery object. This is context. It is a means to provide a reference to the part of the DOM in which the expression should act. By default it is Document – that’s the whole DOM tree from top to bottom – which can be a lot of markup. But by being able to pass in a reference or another $() selector, we can reduce this area signficantly with ease. Suddenly context seems incredibly important, nay essential!

Choosing the right context

Before you jump into existing code and give every selector any old context, bear in mind that it can have negative impact. If you’re already running a slow selector, and pile on another slow selector as context, things are just going to get worse. Take a hypothetical example where we want to find all visible elements with a class of .input-field. These elements happen to be children of a form that exists within an element of class myapp. We could change:

$('.input-field:visible') to $('.input-field:visible', $('.myapp form'))

to provide context, but this would be a step backward. We’re just doubling the amount of work for jQuery.

Since we already know that $('#elementID') is the absolute fastest way to pick out a DOM element, we should aim to always use this as our context. Our selector above would be greatly improved by finding the nearest parent element with an ID (or simply add one to the form!):

$('.input-field:visible', $('#my-form'))

Bingo, it’s all as simple as that – there’s nothing more to it. The expensive $(’.input-field:visible’) now only runs within the form element #my-form, resulting in a significant speed boost and the chance for to add on a ton more awesome features as a result!

Hopefully this shows how you can greatly improve the speed of DOM traversal with jQuery, and the application to javascript in general. In the next part of this entry I hope to show how this can be further extended in some real-world code.

Comments for "jQuery, it's all about context! (part 1)"

Commenting is now closed for this article

  1. It works, just saw a nice speedup after applying this technique. Kudos for a great walkthrough!

  2. Great, glad it was useful. As an addition, I’ve found the jQuery Profile plugin to be really helpful for finding slow selectors that are in need of a bit of context. Saves a lot of time when trying to dig out the bottlenecks.

  3. Great tips, thanks!


beardscratchers.com is a music-focused web experiment and creative-arts journal from London, England.



Previous Entries…

Journal content and design are © of Nick Skelton

built with web standards and a baseline.