The most common use cases of Javascript require no optimization at all. However when you go about building substantial applications in javascript you will hit some walls rather rapidly. Fortunately the code you are currently writing can probably be accelerated substantially.

Step 0: Analyzing performance

Prior to attempting any tweaks, be sure to get firebug and use their console.profile() and console.profileEnd(). Test results will vary substantially during subsequent tests, but they serve their purpose for finding the bottlenecks.

Step 1: Remove Double Dollar $$ and event binding

There are many small performance differences, but several things are likely to really kill performance. One of the most nasty ones is using prototype’s double dollar $$ function (or the similar Element.select). You can often avoid using the double dollar function. For example the use case of attaching events to all ‘report this’ buttons on your site. The simple (and often good enough) approach would be to use the following code:

$$('.report_this').each(function(report_button) {
   var id = report_button.id.split('_')[1];
   report_button.observe('click', this.respondToReportButton.bind(this, id);
});

Four things are slowing this code down: 1. the usage of the $$ function, 2. the usage of each instead of a native looping construct, 3. the retrieving of the id from the id string, 4. the repeated binding of functions.

There are several possible remedies against the above code:

  1. Give all report_this buttons a unique id (say for instance that you have 15 or less in a list)
  2. Pre generate a list of ids using your server side language of choice and pass it to javascript
  3. Manually traverse the DOM; $(‘container’).childNodes can do wonders
  4. Bind once to a common parent element
  5. Find items by name instead of class
  6. Forget about all the initializing and fall back to old school onclick=”classinstance.respondToReportButton()”

This last option sort of goes against many webdevelopment principles, but is often a very pragmatic choice. Joseph Smarr from Plaxo holds a similar opinion on the topic.

A better implementation using technique 1 would be:

this.respondToReportButtonBound = this.respondToReportButton.bind(this);
for(x=1;x<16;x++) {
   button = $('report_button'+x);
   if(!button) break;
   button.observe('click', this.respondToReportButtonBound);
}

In practice I’ve experienced speed improvements of over 40 times using this technique. Firstly the repondTo function is bound only once. Secondly the $$ function is no longer needed. Thirdly the each implementation has been replaced by a blazingly fast for loop. Fourthly the id is no longer extracted. A better practice is to extract it when respondToReportButton is actually executed. This last point actually brings us to the next main item.

Step 2: Be Lazy!

The trick here is to actually put in a bit of effort to make your code lazy. Don’t do anything until it is needed. (With the one, but large exception if doing so would hurt the user experience.) If some items are currently not visible to the user simply don’t bind events to them. If you need to extract id’s don’t do so until someone actually clicks on the item in question. Furthermore also make it lazy in the regular sense of the word. If your code only need to change one item, figure out which one it is and don’t loop about changing all just in case. This point is different for every application, but it can achieve great speed gains for a creative programmer!

Step 3: Stop using prototype functions if you don’t need them

Often you do not really need (as in that it barely saves you development time) some of the functionality of prototype. When comparing the speed of element.innerHTML = ‘hello world’ versus element.update(‘hello world’) the differences are substantial (60 times with large chunks of html). Also the each iterator is often not needed and can be replace by a simple for loop with checks on nodeType and tagName. The same goes for the templating system. These tools barely save you time, but really hurt performance. I am a big fan of the prototype library and for most use cases these nice helpers are great. When you really need speed, be sure to refrain from using them though.

Step 4: Lower level optimizations

When you are done implementing the really important optimizations there are quite some lower level optimizations which will speed up your code.

Two generally great resources on the topic are:

  1. Joseph Smarr (Plaxo)
  2. Opera Dev

The missing performance data

One item affecting the performance of javascript interface elements is the speed by which the browsers redraws the page. Unfortunately I have barely found any documentation on how browsers determine what to redraw and what affects redraw speed. One certainty is the negative influence of transparent images on redraw speed, but other than that little seems to be known. Furthermore most browsers won’t redraw the page until your javascript function returns. This can make your interface elements feel very unresponsive. A solution to this problem is to use the setTimeout functionality.

Message for Dutch Programmers

YouTellMe is an innovative startup located in the center of Rotterdam. Talented programmers are always welcome. Have a look at the job page to get an impression of the job openings (We develop using Python/Django).