jQuery: performance tips

I started reconsidering the performance of my jQuery code after studying the great work of the Yahoo! team, especially some books and tutorials by Nicholas Zakas and Stoyan Stefanov. Most of us often forget that there's a significant difference between a library method and a core DOM method when it comes to performance. $('#test') and document.getElementById('test') perform the same operation, but they're not the same thing. The jQuery method is made up of more steps and lookups than the DOM method. In fact, a call to getElementById() implies only a lookup in the prototype chain of the document object, whereas the jQuery method needs to access the jQuery namespace, then the selector engine, thus transforming the CSS expression either using the querySelector() method (if available) or the aforementioned getElementById() method for backward compatibility with versions of IE prior to 8. So we have 3/4 steps for jQuery against 1.

What's more, core DOM methods are implemented in C/C++ and for that reason are incredibly faster than any library implementation. As a rule of thumb, use jQuery methods and selectors for complex tasks and the DOM for the heavy-lifting of your page. For example, if you have a long list of items, it's impractical to loop over them using each() or $.each(). You can use a normal for loop, which is much faster. Consider the following:

$('td').each(function() {

  var $td = $(this);
  
  var text = $td.text();
  
  if(text.charAt(0) == '$') {
  
    $td.addClass('total');
  
  }

});

This code works well when the performed check on the text node is executed for a certain number of table cells. But when the number of cells grows beyond control, for example because your database size is increasing, then this method shows its limits.

The first thing you can do is using the jQuery context to minimize the number of DOM lookups to select your elements:

$('#payments td').each(function() {

  var $td = $(this);
  
  var text = $td.text();
  
  if(text.charAt(0) == '$') {
  
    $td.addClass('total');
  
  }

});
or:
$('td', '#payments').each(function() {

  var $td = $(this);
  
  var text = $td.text();
  
  if(text.charAt(0) == '$') {
  
    $td.addClass('total');
  
  }

});

Now retest everything: is still slow? Consider that:

  1. More specific selectors are faster than generic selectors. So a.test is faster than .test
  2. Class selectors are not very efficient. You should use ID selectors instead.

Finally, you can rewrite this code using DOM core methods. Since the required task is simply adding a CSS class to an element, you can benefit from a better performance using the following approach:

var doc = document;
var tds = doc.getElementById('payments').getElementsByTagName('td');

var i, len = tds.length;

for(i = 0; i < len; i+= 1) {

  var td = tds[i];
  var text = td.firstChild.nodeValue;
  
  if(text.charAt(0) == '$') {
  
    td.className = 'total';
  
  }

}

As you can see, the steps required inside the loop are exactly the same as those required in the jQuery loop, but with a significant performance improvement.

This entry was posted in by Gabriele Romanato. Bookmark the permalink.

Leave a Reply

Note: Only a member of this blog may post a comment.