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!”.
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:
- 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
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.
Looking at the
jQuery() function signature:
jQuery( expression, [context] )
we see it takes two arguments:
expression– An 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.
In terms of jQuery selectors, this equates to the following order of speed, with everything else trailing pretty far behind:
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
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', $('.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!):
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!