jQuery 11 Performance Tips
jQuery 11 Performance Tips:
Be remember the following tips for increasing the performance by
reducing the work the browser has to do.
1.
Always Descend From an #id
2.
Use Tags Before Classes
3.
Cache jQuery Objects
4.
Harness the Power of Chaining
5.
Use Sub-queries
6.
Limit Direct DOM Manipulation
7.
Leverage Event Delegation (a.k.a. Event Bubbling)
8.
Eliminate Query Waste
9.
Defer to $(window).load
10. Compress
Your JS
11. Learn the
Library
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().
<div id="MyDivID">
<form method="post" action="/">
<h2>Traffic Light</h2>
<ul
id="ulTrafficLight">
<li><input type="radio"
class="TrunOn"
name="light"
value="red"
/> Red</li>
<li><input type="radio"
class="TrunOff"
name="light"
value="yellow"
/> Yellow</li>
<li><input type="radio"
class="TurnOff"
name="light"
value="green"
/> Green</li>
</ul>
<input
class="button"
id="btntraffic"
type="submit"
value="Go"
/>
</form>
</div>
Selecting the button like this is slower:
var traffic_button = $('#MyDivID .button');
Always select the button directly:
var traffic_button = $('#btntraffic');
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 = $('#ulTrafficLight input');
2. Use Tags Before
Classes
The second fastest selector in jQuery is the Tag selector ($('div')).
Again, this is because it maps to a native JavaScript method, getElementsByTagName()
<div id="MyDivID">
<form method="post" action="/">
<h2>Traffic Light</h2>
<ul
id="ulTrafficLight">
<li><input type="radio"
class="TrunOn"
name="light"
value="red"
/> Red</li>
<li><input type="radio"
class="TrunOff"
name="light"
value="yellow"
/> Yellow</li>
<li><input type="radio"
class="TurnOff"
name="light"
value="green"
/> Green</li>
</ul>
<input
class="button"
id="btntraffic"
type="submit"
value="Go"
/>
</form>
</div>
Always prefix a class with a tag name (and remember to descend
from an ID):
var active_light = $('#ulTrafficLight input.TurnOn');
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 ‘MyDivID’ ID:
var content = $('div#MyDivID');
Along the same lines, it is redundant to descend from multiple
IDs:
var traffic_light = $('#MyDivID #ulTrafficLight');
3. Cache jQuery Objects
Get in the habit of saving your jQuery objects to a variable (much
like our examples above). For example, never do this:
$('#ulTrafficLight input.on).bind('click', function(){...});
$('#ulTrafficLight input.on).css('border', '3px dashed yellow');
$('#ulTrafficLight input.on).css('background-color', 'orange');
$('#ulTrafficLight input.on).fadeIn('slow');
Instead, first save the object to a local variable, and continue
your operations:
var $active_light = $('#ulTrafficLight 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.
Note – 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'),
trafficLight :
$('#ulTrafficLight'),
btntraffic :
$('#btntraffic')
};
function doSomething()
{
// 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.someResults = $('#some_ul li');
$my.otherResults = $('#some_table td');
// Use the global functions as you would a normal jQuery
result
$my.otherResults.css('border-color',
'red');
$my.trafficLight.css('border-color',
'green');
}
4. Exploit the Power of
Chaining
var $active_light = $('#ulTrafficLight input.TurnOn');
$active_light.bind('click', function(){...});
$active_light.css('border', '3px dashed yellow');
$active_light.css('background-color', 'orange');
$active_light.fadeIn('slow');
The above code can also be accomplished like this:
var $active_light = $('#ulTrafficLight input.TurnOn');
$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.
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="MyDivID">
<form method="post" action="/">
<h2>Traffic Light</h2>
<ul
id="ulTrafficLight">
<li><input type="radio"
class="TrunOn"
name="light"
value="red"
/> Red</li>
<li><input type="radio"
class="TrunOff"
name="light"
value="yellow"
/> Yellow</li>
<li><input type="radio"
class="TurnOff"
name="light"
value="green"
/> Green</li>
</ul>
<input
class="button"
id="btntraffic"
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 $trafficLight = $('#ulTrafficLight '),
$activeLight = $ trafficLight.find('input.TurnOn'),
$inactiveLights = $ trafficLight.find('input.TurnOff');
Note: You can declare multiple local variables by separating them
with commas – save those bytes!
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 ListesItems = [...], // assume this has 100 unique strings
$mylist = $('#mylist'); // jQuery selects our <ul> element
for (var i=0, l= ListesItems.length; i<l; i++)
{
$mylist.append('<li>'
+ ListesItems [i] + '</li>');
}
Instead, we want to create the entire set of elements in a string before
inserting into the DOM:
var ListesItems = [...], //
assume this has 100 unique strings
$mylist = $('#mylist'), // jQuery selects our <ul>
element
Li_List = "";
// This will store our list items
for (var i=0, l= ListesItems.length; i<l; i++)
{
// storing in a string
variable.
Li_List += '<li>' + ListesItems [i] + '</li>';
}
//Insert after a loop.
$mylist.html(Li_List);
Even faster, we should always wrap many elements in a
single parent node before insertion:
var ListesdItems = [...], // assume this has 100 unique strings
$mylist = $('#mylist'), // jQuery selects our <ul>
element
Ul_List = '<ul id="#mylist">'; // This will store our entire
unordered list
for (var i=0, l= ListesdItems.length; i<l; i++)
{
Ul_List += '<li>'
+ ListesdItems [i] + '</li>';
}
Ul_List += '</ul>'; // Close our unordered list
$mylist.replaceWith(Ul_List);
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.
7.
Leverage Event
Delegation (a.k.a. Event 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:
$('#myList
li).bind('click', function(){
$(this).addClass('clicked');
// do stuff
});
Instead,
we should listen for the click event at the parent level:
$('#myList).bind('click',
function(e){
var target = e.target, // e.target grabs the node that triggered
the event.
$target = $(target); // wraps the
node in a jQuery object
if (target.nodeName === 'LI') {
$target.addClass('clicked');
// do stuff
}
});
Important Note: As of
jQuery 1.7, the .on() method is the preferred method for attaching
event handlers to a document. For earlier versions, the .bind() method is
used for attaching an event handler directly to elements.
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).
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="ulTrafficLight">
<li><input type="radio" class="TurnOn" name="light" value="red" />
Red</li>
<li><input type="radio" class="TurnOff" name="light" value="yellow" />
Yellow</li>
<li><input type="radio" class="TurnOff" name="light" value="green" />
Green</li>
</ul>
<script type="text/javascript>
mylib.trafficLight.init();
</script>
Your Global JS library
would look something like this:
var mylib
=
{
article_page :
{
init : function()
{
// Article page specific
jQuery functions.
}
},
trafficLight :
{
init : function()
{
// Traffic light
specific jQuery functions.
}
}
}
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.
10.
Compress Your JS
This isn’t jQuery related, but very useful technique to make your
javascript lightweight. There is a tendency to make JavaScript functions and
variables overly descriptive, which is essential for developers but irrelevant
to end-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 YUICompressorto
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).
Note: For maximum compression in YUICompressor, always declare
your variables (e.g. var my_long_variable_name;).
11.
Learn the Library
Learn more and more about jQuery. JQuery API Documentation is
available at http://api.jquery.com/. There are many other cheat sheets available on internet to
explore the jQuery API.
Comments
Post a Comment