permalink

117

11 Ways to Increase Your jQuery Performance

 

This post will give you 11 easy steps that are going to instantly improve your jQuery application’s performance, building on top of this excellent post from NetTuts. There isn’t anything that difficult in here and almost anyone can apply these methods to their project. If you’ve got a few extra tips you would like to share, please feel free to include them in the comments section. We’d love to hear from you!.

Update: I recommend reading newer posts on this topic as the below covers earlier versions of jQuery

 

cheatsheet

 

Optimization is something that can help the users of your application achieve actions inside of it much quicker than they may currently be able to. Looking for a way to make that animated drag look smoother? Perhaps you’re accessing it using the least-optimal path. That navigation menu working slow? Perhaps you just need to get rid of some of the waste in your calls. If you feel like your script or jQuery app could use a little performance optimization, read on because this article is sure to help.

 

Before we get started I would like for everyone to remember that you should only strive for optimization when there is a measured need for optimization. Changing neat code to something you or your developers may not be used to is something I would not advise unless you are all well experienced with jQuery and JavaScript syntax. jQuery is still blazingly fast without any performance improvements added (in a lot of cases). Most often, one cannot tell without resorting to metrics or viewing source if it’s the server that delivered these bits in the page, or client-side JavaScript.

 

Some of the tips that in this article will seem straight-forward to some, but I think that giving ourselves a “refresher” is always helpful. I would also like to remind everyone that the man behind jQuery itself (John Resig) strongly recommends that your jQuery applications be heavily performance tested. There are some amazing articles on how to performance test your apps including this one from Resig on performance testing (which comes with a free plugin), this one on Ajax/JSON testing within Visual Studio and Paul Irish’s interesting presentation on Anti-patterns in performance and compression here. If you are going to try improving the performance of your application, guage how long it takes for core functions to be executed, discover what is causing any slow-down (is it the server? is the browser? is it the rendering engine, or is it my code?) and then set out a plan for how you are going to improve your application.

 

With that in mind, let us begin!.

 

jQuery Performance Tip 1: Always Descend From an #id

 

The fastest selector in jQuery is the ID selector ($('#someid')). This is because it maps directly to a native JavaScript method, getElementById().

 
Selecting Single Elements

 

<div id="content">	<form method="post" action="/">		<h2>Traffic Light</h2>		<ul id="traffic_light">			<li><input type="radio" class="on" name="light" 
                          value="red" /> Red</li>			<li><input type="radio" class="off" name="light"
                       value="yellow" /> Yellow</li>			<li><input type="radio" class="off" name="light" 
                            value="green" /> Green</li>		</ul>		<input class="button" id="traffic_button" type="submit" 
              value="Go" />	</form></div>
 

Selecting the button like this is slower:

var traffic_button = $('#content .button');

Instead, select the button directly:

var traffic_button = $('#traffic_button');
 
 
Selecting Multiple Elements

Once we start talking about selecting multiple elements, we are really talking about DOM traversal and looping, something that is slow.

To minimize the performance hit, always descend from the closest parent ID:

var traffic_lights = $('#traffic_light input');
 

jQuery Performance Tip 2: Use Tags Before Classes

 

The second fastest selector in jQuery is the Tag selector ($('head')). Again, this is because it maps to a native JavaScript method, getElementsByTagName().

 

<div id="content">	<form method="post" action="/">		<h2>Traffic Light</h2>		<ul id="traffic_light">			<li><input type="radio" class="on" name="light" 
                             value="red" /> Red</li>			<li><input type="radio" class="off" name="light" 
                        value="yellow" /> Yellow</li>			<li><input type="radio" class="off" name="light" 
                     value="green" /> Green</li>		</ul>		<input class="button" id="traffic_button" type="submit" 
               value="Go" />	</form></div>
 

Always prefix a class with a tag name (and remember to descend from an ID):

var active_light = $('#traffic_light input.on');
 

Note: The class selector is among the slowest selectors in jQuery; in IE it loops through the entire DOM. Avoid using it whenever possible.

 

Never prefix an ID with a tag name. For example, this is slow because it will loop through all <div> elements looking for the ‘content’ ID:

 

var content = $('div#content');
 

Along the same lines, it is redundant to descend from multiple IDs:

var traffic_light = $('#content #traffic_light');
 

jQuery Performance Tip 3: Cache jQuery Objects

 

Get in the habit of saving your jQuery objects to a variable (much like our examples above).

For example, never (eeeehhhhver) do this:

$('#traffic_light input.on).bind('click', function(){...});
$('#traffic_light input.on).css('border', '3px dashed yellow');
$('#traffic_light input.on).css('background-color', 'orange');
$('#traffic_light input.on).fadeIn('slow');

Instead, first save the object to a local variable, and continue your operations:

var $active_light = $('#traffic_light input.on');
$active_light.bind('click', function(){...});
$active_light.css('border', '3px dashed yellow');
$active_light.css('background-color', 'orange');
$active_light.fadeIn('slow');
 
 

Tip: Since we want to remember that our local variable is a jQuery wrapped set, we are using $ as a prefix.

 

Remember, never repeat a jQuery selection operation more than once in your application.

 

Bonus Tip – Storing jQuery results for later

 

If you intend to use the jQuery result object(s) in another part of your program, or should your function execute more than once, cache it in an object with a global scope.

 

By defining a global container with jQuery results, we can reference them from within other functions:

 

// Define an object in the global scope (i.e. the window object)window.$my ={	// Initialize all the queries you want to use more than once	head : $('head'),	traffic_light : $('#traffic_light'),	traffic_button : $('#traffic_button')};
function do_something(){	// Now you can reference the stored results and manipulate them	var script = document.createElement('script');	$my.head.append(script);
	// When working inside functions, continue to save jQuery results	// to your global container.	$my.cool_results = $('#some_ul li');	$my.other_results = $('#some_table td');
	// Use the global functions as you would a normal jQuery result	$my.other_results.css('border-color', 'red');	$my.traffic_light.css('border-color', 'green');}
 

jQuery Performance Tip 4: Harness the Power of Chaining

 

The previous example can also be accomplished like this:

 

var $active_light = $('#traffic_light input.on');
$active_light.bind('click', 
 function(){...})	.css('border', '3px dashed yellow')	.css('background-color', 'orange')	.fadeIn('slow');
 

This allows us to write less code, making our JavaScript more lightweight.

 

jQuery Performance Tip 5:  Use Sub-queries

jQuery allows us to run additional selector operations on a wrapped set. This reduces performance overhead on subsequent selections since we already grabbed and stored the parent object in a local variable.

 

<div id="content">	<form method="post" action="/">		<h2>Traffic Light</h2>		<ul id="traffic_light">			<li><input type="radio" class="on" name="light" 
                        value="red" /> Red</li>			<li><input type="radio" class="off" name="light" 
                       value="yellow" /> Yellow</li>			<li><input type="radio" class="off" name="light" 
                   value="green" /> Green</li>		</ul>		<input class="button" id="traffic_button" type="submit" value="Go" />	</form></div>
 

For example, we can leverage sub-queries to grab the active and inactive lights and cache them for later manipulation.

 

var $traffic_light = $('#traffic_light'),	$active_light = $traffic_light.find('input.on'),	$inactive_lights = $traffic_light.find('input.off');
 

Tip: You can declare multiple local variables by separating them with commas – save those bytes!

 

jQuery Performance Tip 6: Limit Direct DOM Manipulation

 

The basic idea here is to create exactly what you need in memory, and then update the DOM. This is not a jQuery best practice, but a must for efficient JavaScript. Direct DOM manipulation is slow.

 

For example, if you need to dynamically create a list of elements, do not do this:

 

var top_100_list = [...], // assume this has 100 unique strings$mylist = $('#mylist'); // jQuery selects our <ul> element
for (var i=0, l=top_100_list.length; i<l; i++){	$mylist.append('<li>' + top_100_list[i] + '</li>');}
 

Instead, we want to create the entire set of elements in a string before inserting into the DOM:

 

var top_100_list = [...], // assume this has 100 unique strings$mylist = $('#mylist'), // jQuery selects our <ul> elementtop_100_li = ""; // This will store our list items
for (var i=0, l=top_100_list.length; i<l; i++){	top_100_li += '<li>' + top_100_list[i] + '</li>';}
$mylist.html(top_100_li);
 

Even faster, we should always wrap many elements in a single parent node before insertion:

 

var top_100_list = [...], // assume this has 100 unique strings	$mylist = $('#mylist'), // jQuery selects our <ul> element	top_100_ul = '<ul id="#mylist">'; 
// This will store our entire unordered list
for (var i=0, l=top_100_list.length; i<l; i++){	top_100_ul += '<li>' + top_100_list[i] + '</li>';}top_100_ul += '</ul>'; // Close our unordered list
$mylist.replaceWith(top_100_ul);
 

If you do the above and are still concerned about performance:

  • Give jQuery’s clone() method a try. This creates a copy of the node tree, which you can manipulate “off-line” and then insert back in when you are ready.
  •  
  • Use DOM DocumentFragments. As the creator of jQuery points out, they perform much better than direct DOM manipulation. The idea would be to create what you need (similar to what we did above with a string), and use the jQuery insert or replace methods.
  •  

jQuery Performance Tip 7:  Leverage Event Delegation (a.k.a. Bubbling)

 

Unless told otherwise, every event (e.g. click, mouseover, etc.) in JavaScript “bubbles” up the DOM tree to parent elements. This is incredibly useful when we want many elements (nodes) to call the same function.

Instead of binding an event listener function to many nodes—very inefficient—you can bind it once to their parent, and have it figure out which node triggered the event.

 

For example, say we are developing a large form with many inputs, and want to toggle a class name when selected.

 

A binding like this is inefficient:

 

$('#entryform input).bind('focus', function(){	$(this).addClass('selected');}).bind('blur', function(){	$(this).removeClass('selected');});

Instead, we should listen for the focus and blur events at the parent level:

$('#entryform).bind('focus', function(e){	var cell = $(e.target);  
// e.target grabs the node that triggered the event.	cell.addClass('selected');}).bind('blur', function(e){	var cell = $(e.target);	cell.removeClass('selected');});
 

The parent node acts as a dispatcher and can then do work based on what target element triggered the event.

 

If you find yourself binding one event listener to many elements, you are doing something wrong (and slow).

 

jQuery Performance Tip 8: Eliminate Query Waste

 

Although jQuery fails nicely if it does not find any matching elements, it still takes time to look for them. If you have one global JavaScript for your entire site, it may be tempting to throw every one of your jQuery functions into $(document).ready(function(){ // all my glorious code }).

Don’t you dare.

 

Only run functions that are applicable to the page. The most efficient way to do this is to use inline initialization functions so your template has full control over when and where JavaScript executes.

For example, in your “article” page template, you would include the following code before the body close:

 

<script type="text/javascript>mylib.article.init();</script></body>
 

If your page template includes any variety of modules that may or may not be on the page, or for visual reasons you need them to initialize sooner, you could place the initialization function immediately after the module.

 

<ul id="traffic_light">	<li><input type="radio" class="on" name="light" value="red" /> Red</li>	<li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>	<li><input type="radio" class="off" name="light" value="green" /> Green</li></ul><script type="text/javascript>mylib.traffic_light.init();</script>
 

Your Global JS library would look something like this:

 

var mylib ={	article_page :	{		init : function()		{			// Article page specific jQuery functions.		}	},	traffic_light :	{		init : function()		{			// Traffic light specific jQuery functions.		}	}}
 

jQuery Performance Tip 9: Defer to $(window).load

 

There is a temptation among jQuery developers to hook everything into the $(document).ready pseudo event. After all, it is used in most examples you will find.

Although $(document).ready is incredibly useful, it occurs during page render while objects are still downloading. If you notice your page stalling while loading, all those $(document).ready functions could be the reason why.

 

You can reduce CPU utilization during the page load by binding your jQuery functions to the $(window).load event, which occurs after all objects called by the HTML (including <iframe> content) have downloaded.

 

$(window).load(function(){	// jQuery functions to initialize after the page has loaded.});
 

Superfluous functionality such as drag and drop, binding visual effects and animations, pre-fetching hidden images, etc., are all good candidates for this technique.

 

jQuery Performance Tip 10: Compress Your JS

 

Okay, this isn’t jQuery related, but I had to include it. There is a tendency to make JavaScript functions and variables overly descriptive, which is essential for developers but irrelevant to users.

No more excuses, it’s time to build JS compression into our workflows. Comment the heck out of your code, and run it through a compression tool before launching to production.

 

Use YUICompressor to squeeze out wasteful bytes from your code. In our experience, it safely compresses JavaScript as small as it can possibly get without a CPU penalty (such as Base62 encoding with Packer).

 

Tip: For maximum compression in YUICompressor, always declare your variables (e.g. var my_long_variable_name;).

 

jQuery Performance Tip 11:  Learn the Library!

 

Print out this jQuery 1.3 cheat sheet, and make it a goal to eventually understand what each function does. If you find yourself repeating yourself repeating, there is probably an easier (and more efficient) way. jQuery 1.4 will hopefully be released right around the corner, but this should prove an excellent resource until then.

 

jquery cheat sheet

117 Comments

Leave a Reply

Required fields are marked *.

*