Jquery Performance Tuning

After searching and digging a lot of artciles about improving jquery performance in web apps, I decided to make a list of best common performance tips and bect practices. We’ll also reveal some of JQuery’s hidden feature and how we can use them for performance tuning.

Lets divide this post into 4 main categoris.

  • Selector Performace
  • DOM Manipulation
  • Events
  • Digging into JQuery Secrets
  • General and All-Time performance tricks

Selector Performace

Know your selectors

You should be careful about using jquery selectors because all selectors are nor created equally. With benchmarks, we can see:

  $('#id, element'); // Id and Element selectors are the fastest
  // the reason is because it maps to a native JavaScript methods
  $('.class_name'); // Slower class selectors
  $(':hidden, [attribute=value]'); // Pseudo and Attribute selectors are the slowest

Optimize selectors for Sizzle’s ‘right to left’ model

JQuery as of now uses the Sizzle Javascript Selector Library which works a bit differently from the selector engine used in the past versions. Namely it uses a ‘right to left’ model rather than a ‘left to right’. Make sure that your right-most selector is really specific and any selectors on the left are more broad:

  var linkContacts = $('.contact-links div.side-wrapper'); // Good
  var linkContacts = $('a.contact-links .side-wrapper'); // Bad

Keep it Simple!

Avoid overly complex selectors. Unless you have an incredibly complex HTML document, it’s rare you’ll need any more than two or three qualifiers.

  $("body #page:first-child article.main p#intro em"); // Bad
  $("p#intro em"); // Good

Class selectors and Context

Always try to scope your selectors with id or element.

  $('.active').show(); // bad
  $('#header .active').show(); // better
  $('.active', '#header').show(); // better
  $('#header').find('.active').show(); // fastest
  // .find() is faster because it directly uses native javascript methods to search inside the passsed context under the hood
  $('button.active'); // fastest
  // grabs all matches tags FIRST, then iterates through them to find matching classes

DOM Manipulation

Cache and Chains to Avoid Reselection

Interacting with the DOM as little as possible will drastically speed up your applications.

  // Bad
  $('#sidebar .promo').hide();
  $('#sidebar .newPromo').show();
  $('#sidebar').after(somethingcool);

  // Good
  var sb = $('#sidebar');
  sb.find('.promo').hide();
  sb.find('.newPromo').show();
  sb.after(somethingcool);

  // Bad
  $("p").css("color", "blue");
  $("p").css("font-size", "1.2em");
  $("p").text("Text changed!");

  // Good
  $("p").css({ "color": "blue", "font-size": "1.2em"}).text("Text changed!");

Wrap everything in a single element when doing any kind of DOM insertion

DOM manipulation is very slow. Try to modify your HTML structure as little as possible.

  var menu = '<ul id="menu">';
  for (var i = 1; i < 100; i++) {
      menu += '<li>' + i + '</li>';
  }
  menu += '</ul>';
  $('#header').prepend(menu);

  // Instead of doing:

  $('#header').prepend('<ul id="menu"></ul>');
  for (var i = 1; i < 100; i++) {
      $('#menu').append('<li>' + i + '</li>');
  }

Events

Leverage Event Delegation

Event listeners cost memory. In complex websites and apps it’s not uncommon to have a lot of event listeners floating around, and thankfully jQuery provides some really easy methods for handling event listeners efficiently through delegation.

When you have a lot of elements in a container and you want to assign an event to all of them – use delegation to handle it. Delegation provides you with the ability to bind only one event to a parent element and then check on what child the event acted (target). It’s very handy when you have a big table with a lot of data and you want to set events to the TDs. Grab the table, set the delegation event for all the TDs:


  $("table").delegate("td", "hover", function(){

    $(this).toggleClass("hover");

  });

Digging into JQuery Secrets

Element Creation

The following snippet is enough what I mean:


  var myDiv = jQuery('<div/>', {
    id: 'my-new-element',
    class: 'foo',
    css: {
      color: 'red',
      backgrondColor: '#FFF',
      border: '1px solid #CCC'
    },
    click: function() {
      alert('Clicked!');
    },
    html: jQuery('<a/>', {
      href: '#',
      click: function() {
        // do something
        return false;
      }
    })
  });

Writing own selectors

If you have selectors that you use often in your script – extend jQuery object $.expr[’:’] and write your own selector. In the following example We create a selector above_the_fold that returns a set of elements that are not visible:


  $.extend($.expr[':'], {
    above_the_fold: function(el) {
      return $(el).offset().top < $(window).scrollTop() + $(window).height();
    }
  });
  var nonVisibleElements = $('div:above_the_fold'); // Select the elements

CSS Hooks

The CSS hooks API was introduced to give developers the ability to get and set particular CSS values. Using it, you can hide browser specific implementations and expose a unified interface for accessing particular properties.


  $.cssHooks['borderRadius'] = {
    get: function(elem, computed, extra){
      // Depending on the browser, read the value of
      // -moz-border-radius, -webkit-border-radius or border-radius
    },
    set: function(elem, value){
      // Set the appropriate CSS3 property
    }
  };

  // Use it without worrying which property the browser actually understands:
  $('#rect').css('borderRadius',5);

What is even better, is that people have already built a rich library of supported CSS hooks that you can use for free in your next project.

General And All time tricks

Use HTML5

You may wonder how jquery performance and HTML5 are related but the new HTML 5 standard comes with new tags and a lighter DOM structure in mind. Lighter DOM structure and different tags diferent purpose for means less elements to traverse for jQuery.

DOM isn’t your database

Traversing DOM to retrieve information stored in .text(), .html() is not optimal approach. Use html5 data to attach any kind of information in DOM. With the recent updates to the jQuery data() method, HTML5 data attributes are pulled automatically and are available as entries.

  <div id="d1" data-role="page" data-page_hash="H4jk8s00">
  </div>

  $("#d1").data("role"); //page
  $("#d1").data("page_hash"); //H4jk8s00

Append style tag when styling 15 or more elements

When styling a few elements it is best to simply use jQuery’s css() method, however when styling 15 or more elements it is more efficient to append a style tag to the DOM instead of applying css to each items.

  $('<style type="text/css"> div.activated { color: red; } </style>').appendTo('head');

Always use the latest version

JQuery is always in constant development and improvement. The latest version always contains more bug fixes and performance improvements so it is always wise to keep upto date with the latest versions.

Combine jQuery with raw Javascript where needed

  $('#someAnchor').click(function() {
    alert( $(this).attr('id') );
  });

  $('#someAnchor').click(function() {
    alert( this.id );
  });

Load the framework from Google Code

I urge everyone to use the Google AJAX Libraries content delivery network to serve jQuery to users directly from Google’s network of datacenters. Doing so has several advantages over hosting jQuery on your server(s): decreased latency, increased parallelism, and better caching.

The only disadvantage is that if the connection to the CDN were to fail, our site would be left with no jQuery library which could be a big deal if we run a jQuery intensive website. If it happens then there should be some mechanism to fetch the library from other source or from our own locally hosted location. Fortunately, you can easily reference a backup. You can have a copy of jQuery on your server, and use a little bit of JavaScript to load it only if the Google one doesn’t load for some reason. This little snippet, found in HTML5 Boilerplate, will do just that:

  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
  <script>window.jQuery || document.write('<script src="js/libs/jquery-1.9.0.min.js"><\/script>')</script>

Comments