Wednesday, October 27, 2010

Optimising jQuery Selectors

Is your jQuery slow? Is there a noticeable delay when your jQuery effect runs? Check your selectors. jQuery has made Javascript so much easier to write, but easier to get some things wrong too - particularly through over-use of selectors whereby a piece of code spends too much time scanning the DOM. Here are some techniques to help optimise your use of selectors in your jQuery code.

  1. Select by ID wherever possible - use the speed hierarchy.
  2. Don't repeat yourself (DRY). Store results of a selector in a variable if you need to refer to it again.

Select by ID

When you select by ID ($("#some-id")) jQuery will use the "native Javascript" call getElementById() which is very fast and will stop scanning after finding the first result. [3]

If you cannot select an element by ID (perhaps because you are looking for multiple elements), can you narrow down the search by selecting a parent element by ID? jQuery will have to scan for the parent but has a smaller search to execute for the child. For example, $("#some-form-id .mandatory") will be faster than $(".mandatory"). Why? Because the $(".mandatory") selector forces jQuery to visit every single element in the DOM to find all elements with the class "password", whereas the $("#some-form-id .mandatory") narrows down that search to children of the element with ID "some-form-id".

Don't Repeat Yourself

If you find yourself referring to the same element mutliple times using the same jQuery selector, try to turn it into a variable instead. Keep in mind that each time you use a jQuery selector, you are running a search command. So, instead of having multiple instances of $("#some-id") in your code, have one $someElement = $("#some-id") and multiple instances of $someElement.

On a side note, what is the significance of the dollar sign in the variable name $someElement? Is it some supercool jQuery magic? No! $someElement and someElement are both valid JavaScript variable names, and the dollar sign isn't some special operator - it is denoting that the object being referred to was created by jQuery. This is important because the jQuery object you get back from $("#some-id") is different to the DOM element you get back from document.getElementById("some-id"): the former has a "jQuery wrapper".

The Speed Hierarchy

In general, everything revolves around the fact that Javascript/jQuery scans through the DOM every time you ask it to look for something, so you want to choose selectors that make that scanning job fast and small.

The "speed hierarchy" goes like this, in order of fastest to slowest [1]:

  1. ID: $("#some-id")
  2. Element: $("div")
  3. Class: $(".some-class")

As mentioned before, when you select by ID ($("#some-id")) jQuery will use the "native Javascript" call getElementById() which is very fast and will stop scanning after finding the first result. [3]

Selecting by element (tag name) is next fastest because it uses the native call getElementsByTagName() to narrow down the search. For example, searching for $("div .password") will limit the search for element with class "password" to all DIV elements (retrieved by getElementsByTagName()).

Selecting by class is slowest because, as mentioned above, a search like that ($(".mandatory")) forces jQuery to visit every single element in the DOM to find all elements with the class "password".

$= means "ends with". But... even faster than $("#some-id") is $("div[id$=some-id]"). It is faster because div in $("div[id$=some-id]") is telling jQuery to use ignore elements that aren't DIVs and $= in $("div[id$=some-id]") is the "single selector" operator, telling jQuery to stop looking after it's found the first match.

Resources

Sites that helped me with this page.