3 Simple Things to Make Your jQuery Code Awesome

Make jQuery AwesomejQuery is one of the most popular (if not the most) JavaScript libraries in existence and a great number of people use it to do some amazing things. Personally, jQuery is what got me excited for learning JavaScript. The problem is that a lot of programmers don’t understand that with all that power massive amounts of CPU cycles are used. As much as jQuery engineers try to optimize jQuery, they are always limited in how fast they can make it go. There are a few things that you, as a user of jQuery, can do to make your jQuery code faster.

1 – jQuery Object Caching

Caching your jQuery objects may possibly be the best thing you can do to cut your code down to run leaner and meaner. If you use the same selector more than once in your code, or if you query the DOM inside a loop or in a function that is run more than once, then you can probably benefit a lot from caching your queries inside a variable. Consider the following 3 examples:

  1. // Loop
  2. for (var i=0; i<100; i++) {
  3.     $('ul.special').append('<li>'+i+'</li>');
  4. }
  5.  
  6. // Multiple Functions that each have the
  7. // chance to be called mulple times
  8. $('#showbutton').on('click', function() {
  9.     $('#box').show();
  10. });
  11. $('#hidebutton').on('click', function() {
  12.     $('#box').hide();
  13. });
  14.  
  15. // Just re-querying
  16. $('p').width(150);
  17. $('p').css('color', 'red');
  18. $('p').addClass('awesome');

In all of these cases, you could save some DOM searching trips by assigning the jQuery object to a variable (generally prefixed with a dollar sign to distinguish it as a jQuery object), like so:

  1. var $ul = $('ul.special');
  2. for (var i=0; i<100; i++) {
  3.     $ul.append('<li>'+i+'</li>');
  4. }
  5.  
  6. var $box = $('#box');
  7. $('#showbutton').on('click', function() {
  8.     $box.show();
  9. });
  10. $('#hidebutton').on('click', function() {
  11.     $box.hide();
  12. });
  13.  
  14. $('p').width(150).css('color', 'red').addClass('awesome');

One of the most expensive operations you can do is query the DOM, especially in older browsers that can’t be optimized for with built-in functions. Each time you query the DOM (with a few exceptions), you have to search through the entire DOM to find each and every matching element, which can take time, especially on large documents.  The third example actually uses chaining, which is similar to caching because you still optimize down to one DOM search, but it doesn’t require you to save the jQuery object to a variable.

2 – Selector Optimization

The CSS selectors used in your DOM queries can sometimes make more of a difference in performance than the lack of caching the results of that search. The first thing you have to realize is that the selectors are read from right to left, so you always want your most specific selectors (most notably id’s) to be as far to the right as possible. Many times, though, you are trying to find the children of an element with an id, therefore you can’t have the id selector furthest to the right in the full selector. There is a way around this though, via context or using find or children:

  1. // Instead of this:
  2. $('#id p');
  3.  
  4. // Try one of these:
  5. $('p', '#id');
  6. $('#id').find('p');
  7. $('#id').children('p')

The following selectors are ranked from fastest to slowest. Always try to have a faster selector further to the right or within the context parameter/find/children to make your selections as fast as possible.

1)      $('#id');
An id selector is easily the fastest selector. There are two reasons for this: 1) There is only ever one element with an id, so once it is found, the searching stops and 2) browsers have a built in function for searching for elements by their id (document.getElementById()), and built-in functions run much quicker than hand-made functions.

2)      $('tag ');
Searching by a tag name is somewhat fast only because all browsers support the built-in function called document.getElementsByTagName().

3)      $('.class');
Searching via class would probably be comparable to searching by the tag name, but you have to be careful just because IE8 and below don’t support the native document.getElementsByClassName().

4)      $('[attribute]'); or $('[attribute=value]'); or $(':pseudo');
No browser currently has a native function available to JavaScript for searching with these selectors, so jQuery is required to crawl through the DOM by itself and check each element to see if it matches this selector. There are some modern browsers that have document.querySelectorAll(), which is a native function that can take any selector, but even with the increased performance from this function, the look-ups for these selectors is still quite slow.

3 – Delegating Events

The third and final optimization involves events. If you are attaching an event handler to each and every cell of a table, you could be using a lot more memory than you really need to, plus it takes a little time to apply a handler to each of those cells. This might be done something like this:

  1. $('table td').on('click', function() {
  2.     // Do Something
  3. });

Event delegation allows us to attach a single event handler to a parent element – saving us memory and set up time – that only fires when the event is triggered on specific child elements. So, using the example above, we could attach a single handler to the table that would fire any time someone clicked on a td, but not when someone clicks on a th or other element within the table. The code to do this looks like this:

  1. $('table').on('click', 'td', function() {
  2.     // Do Something
  3. });

Notice that the selector for the elements that you actually want the event to trigger on is now the second argument to the on function. The cool thing is that this still refers to the td that was clicked and not to the table, just like it would if you had attached the handler directly to the cells. This also has the added benefit that if more table cells are dynamically added in, you don’t need to add the event handler to them because they are already covered by the parent element.

Concluding the Optimizations

So there’s 3 ways to be a more awesome jQuery programmer. I know this topic has been discussed countless times around the internet, but you never know when you’ll run into someone who hasn’t heard of these. Besides, it never hurts to be reminded. So, remember to cache, use fast selectors, and delegate your events to earn the respect of jQuery programming pros everywhere. Also remember to comment and/or share. Happy Coding!

No related posts.

  • Fábio M. Costa

    Nice post as always.
    Just note that on number 2, Selector Optimization, there’s no big difference between $(‘#id p’) and $(‘p’, ‘#id’), Sizzle auto detects this id selector at the left of your selector and transforms it on the selection context. Look at these lines: https://github.com/jquery/sizzle/blob/master/sizzle.js#L87-96

    • http://www.joezimjs.com Joe Zimmerman

      This is absolutely false. Check out this test on JSPerf: http://jsperf.com/jquery-selector-perf-right-to-left/70
      Even if Sizzle is attempting to optimize this, it isn’t working as of jQuery 1.7.1. This is probably because it has to do more work to separate out the selectors for the optimization. Either way, it will probably always be faster to use $(‘p’,'#id’).

      • Fabio Miranda Costa

        That’s interesting, thank’s for the link!
        So looks like getElementById + getElementsByTagName is faster than using a single call to querySelectorAll. Just Opera and IE8 are faster on querySelectorAll. Browsers vendors need to optimize it.

  • Stefan Matsson

    Good tips for beginners. :) Your first example can be optimized even more by not updating the DOM 100 times. Each time you update the DOM the browser needs to “rebuild” its internal structure of the page. The recommended solution is to use an array and push each element (the li string) to it and then update the DOM once all items have been added, as described in the last test here (“string array”): http://jsperf.com/dom-insertion/2 

    • http://www.joezimjs.com Joe Zimmerman

       I totally agree. That is a good point. I was only trying to demonstrate the caching and by using the method that I showed above instead of optimizing even further by taking your suggestions I was able to prove the caching point. If I had converted it to what you did, it would not have even needed caching.
      In any case, I recommend that everyone still takes your advice because DOM manipulation can be even more taxing than DOM searching depending on the amount of manipulation and the size of the document being searched.