Web Development - JavaScript Optimization

December 12, 2011

So you have a fast website. You've optimized your database queries and you're using the most efficient page caching mechanisms. The users of your site are pretty satisfied, but you want just a little bit more. How can you push your site to the next level by making sure you've included all the optimizations you can to get the fastest speed possible?

Optimize your JavaScript.

You might not be concerned with optimizing you code because newer browsers have gotten significantly faster at processing JavaScript. But like all other programming languages, there are minor tweaks you can make that provide a significant performance gain. Let's take a look at a few of the ways you can be certain that you're getting the most out of your client-side JavaScript code.

JavaScript in External Files
The first optimization is to write your JavaScript in external files. By using external files, your browser is able to cache your code. This prevents users from needing to re-download your JavaScript during every page load and potentially during every AJAX request. When a browser visits any website, it checks the src attribute in the <script> tags and then determines if it already has a copy of that file on your computer. If it does, it won't spend wasteful time re-downloading the exact same code. If you include your code directly in your HTML within <script> tags, it will download that same code each time so that it can be processed. Save those precious bytes!

Minification
Now that you're putting your code in external files, your next goal is to reduce the amount of time it takes to initially download that code so that it can be cached by the browser. This is where minification comes into play. Minification (or minimization) is the process of removing all unnecessary characters from source code (without changing its functionality). Essentially this means you'll remove whitespace characters and comments.

Unless you like to torture yourself (and those who follow your work), you probably write code that's pretty readable. This includes splitting code up between multiple lines, indenting your code with spaces or tabs, and writing comments that explain some esoteric portion of code. All these items are not important to the JavaScript engine because it will just ignore them, but it still takes time to download these extra bytes that will never get used.

Luckily for you, you don't have to spend time creating the tool that will remove these unneeded characters. There are several good tools out there that will minify your code, and I recommend YUI Compressor. This tool can reduce your code by up to 60% (depending upon how efficiently you write your code). In addition to removing all the unnecessary characters, YUI Compressor will rewrite your code to use smaller variable names. If you have a local variable in a function called var myDescriptiveVariable, the compressor will rename it to var a. We just took our 21-character variable name down to 1 character! If this variable is used in 10 places, we've trimmed 200 characters with this one variable, and this happens for every local variable in your script! That's a lot of bytes saved.

If you're paying close attention, the key takeaway you'll notice is that using local variables (compared to global variable that don't get minified) is one great way to reduce the size of your code after minification occurs.

Initial Page Load Operations
Now that your code has been minified, let's take a look at initial page load operations. Since some JavaScript operations are synchronous and will halt other browser processing, we want to make sure we don't slow down the page when the user is trying to perform an action. There are two things we can do to improve initial page loading performance. First, reduce the amount of code you actually invoke on page load (or when the DOM is ready for interaction using Mootools' domready event, jQuery's document ready event or your favorite framework's equivalent). Second, implement lazy-loading techniques. For example, instead of adding all your events to all the elements on the page initially, wait for user interaction or some other process to add the events. Let's look at a few specific examples so you can see what I mean. Note: the code samples are using Mootools syntax.

Before:

var comments = $('.articles > div.comments');
$('showComments').addEvent('click', function() { comments.show(); });

After:

$('showComments').addEvent('click', function() { $('.articles > div.comments').show(); });

While this may improve initial page load, each time we click on showComments, we have to parse the DOM (Document Object Model) to find all the comments, and this is not a cheap operation. The key here is test your own code and determine with each scenario which way is faster and which will keep your users the happiest. Just realize that on demand operations may be more acceptable to users than having to wait for the page to load. You be the judge!

Code Caching
We discussed file caching before, but what about code caching? We need to determine which operations will benefit the most from caching. The two items I will focus on are loops and DOM interactions. Loops can be costly because you may be performing unnecessary actions within each iteration.

Before:

for (var i=0; i<$$('.toggle').length; i++) {
 var el = new Element('div.something', {
 html: $$('.toggle')[i].get('text')
 });
 el.inject($('content'));
}

After:

var i = 0,
 els = $$('.toggle'),
 length = els.length,
 container = new Element('div');
 
for (i=0; i<length; i++) {
 new Element('div.something', {
 html: els[i].get('text')
 }).inject(container);
}
 
container.inject($('content'));

The "Before" loop is inefficient because we are querying the DOM three separate times to get the elements we need (twice for .toggle elements and once for the #content element), and we are having to determine the length property of that collection of elements during each iteration. In our case, the length won't change, so we only need to find it one time. Finally, during each iteration, we add a new element to the #content div, and this causes a redraw of the page. DOM manipulation and redrawing can be expensive, so let's look at how the second method is so much more efficient.

We start by caching our DOM elements so we only have to pull them once and get the length property of those elements only once. We've also created an element which will be used to contain all our new elements. Since the container has not been added to the DOM yet, we can add and remove from it without having to worry about the expensive redraw of the page. Once all our elements have been added to the container, we then inject the container into the #content div. Since this is only happening once, we have significantly improved performance.

Selector Efficiency
The last optimization technique I'll review is selector efficiency. Selectors are used to grab specific elements from the document in order to interact with them in your code. It's very possible to write poor-performing selectors. Some selectors to avoid:

$$('*')

This will grab all the elements in your document. If you have a huge document, this could take a while. You better not be using this selector in a loop!

$$('[data-some-attribute]')

This selector is inefficient because it has to look for this data attribute within every element in the document. If there are thousands of elements and each has several data attributes, you should just go get some coffee.

$$('body .person:nth-child(3n+1) .category p span.title')

This selector is fairly complicated. The reason this can be inefficient is because it may have to unnecessarily inspect many elements to get to the one(s) it needs. Determine if you can simplify the selector by being more specific and using an id to get to elements or even consider slightly modifying your HTML so that it's easier to create efficient selectors.

There are plenty of other techniques that will help improve the speed and efficiency of your JavaScript, but these are a good start and should help make you and your users happy. Remember that DOM interactions are slow and expensive, so do what you can to reduce chatting back and forth with the document. Check your loops and minify your code, and your users will surely return.

Happy coding!

-Philip

Comments

December 12th, 2011 at 4:26pm

What does this article have to say about HTML5??? That's what the title starts off with....

December 13th, 2011 at 10:23am

No HTML5. Ticks in code examples aren't valid. No mention of the closure compiler or uglify. No JS best practices.

December 13th, 2011 at 10:43am

Thanks for the comments, guys!

This post is "Part I" in the series Philip is writing, so it will include tips about HTML5 and other Web Development tips in the next installments. The Javascript optimization tips shouldn't be taken as a "be all, end all" of Javascript best practices, but they will help many developers start down that path.

Thanks for referencing closure compiler and uglify, Chad! Those resources were also on the list to include but didn't make the cut simply due to the fact that the post was already 1,300+ words.

December 13th, 2011 at 11:45am

You need to mention that $(".myclass") also searches through all nodes, but even worse executes a regex split on the class attr on each one. Actually youre doing this mistake yourself several times in this article :-)

Also, > is slower than not using it, have it only in cases of ambiguity.

Also, * selector is not slow at all as it is built in the browser natively, . getElementsByTagName("*")

December 13th, 2011 at 2:17pm

@Tim As Kevin mentioned, this is only part 1. The title was to summarize the series as a whole. Look for more to come at a later point.

@Chad Not sure how the ticks got in there. They are supposed to be single quotes.

@b Thanks for your explanations. One could write lengthy books on selectors, but this was just a very small snippet in an article that covers several topics. It was supposed to be high level.

December 13th, 2011 at 2:25pm

I think the ticks were auto-formatted by a word processing doc in one of the drafts, and when the post was published, we didn't catch them to make sure they were correctly shown as single quotes... I went back through and replaced the misformatted characters I could find. Did that update resolve the code validity issue?

December 15th, 2011 at 12:19am

Great post but where is html5?

December 15th, 2011 at 2:15pm

@Amrinder Per my previous comment to Tim, this was only part 1. There will be more to come that include HTML5-related items. Thanks for reading!

Leave a Reply

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • You can enable syntax highlighting of source code with the following tags: <pre>, <blockcode>, <bash>, <c>, <cpp>, <drupal5>, <drupal6>, <java>, <javascript>, <php>, <python>, <ruby>. The supported tag styles are: <foo>, [foo].
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.

Comments

December 12th, 2011 at 4:26pm

What does this article have to say about HTML5??? That's what the title starts off with....

December 13th, 2011 at 10:23am

No HTML5. Ticks in code examples aren't valid. No mention of the closure compiler or uglify. No JS best practices.

December 13th, 2011 at 10:43am

Thanks for the comments, guys!

This post is "Part I" in the series Philip is writing, so it will include tips about HTML5 and other Web Development tips in the next installments. The Javascript optimization tips shouldn't be taken as a "be all, end all" of Javascript best practices, but they will help many developers start down that path.

Thanks for referencing closure compiler and uglify, Chad! Those resources were also on the list to include but didn't make the cut simply due to the fact that the post was already 1,300+ words.

December 13th, 2011 at 11:45am

You need to mention that $(".myclass") also searches through all nodes, but even worse executes a regex split on the class attr on each one. Actually youre doing this mistake yourself several times in this article :-)

Also, > is slower than not using it, have it only in cases of ambiguity.

Also, * selector is not slow at all as it is built in the browser natively, . getElementsByTagName("*")

December 13th, 2011 at 2:17pm

@Tim As Kevin mentioned, this is only part 1. The title was to summarize the series as a whole. Look for more to come at a later point.

@Chad Not sure how the ticks got in there. They are supposed to be single quotes.

@b Thanks for your explanations. One could write lengthy books on selectors, but this was just a very small snippet in an article that covers several topics. It was supposed to be high level.

December 13th, 2011 at 2:25pm

I think the ticks were auto-formatted by a word processing doc in one of the drafts, and when the post was published, we didn't catch them to make sure they were correctly shown as single quotes... I went back through and replaced the misformatted characters I could find. Did that update resolve the code validity issue?

December 15th, 2011 at 12:19am

Great post but where is html5?

December 15th, 2011 at 2:15pm

@Amrinder Per my previous comment to Tim, this was only part 1. There will be more to come that include HTML5-related items. Thanks for reading!

Leave a Reply

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • You can enable syntax highlighting of source code with the following tags: <pre>, <blockcode>, <bash>, <c>, <cpp>, <drupal5>, <drupal6>, <java>, <javascript>, <php>, <python>, <ruby>. The supported tag styles are: <foo>, [foo].
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.