Wednesday, September 27th, 2006

Lessons in JavaScript Performance Optimization

Category: JavaScript

Fellow Ajaxian, Michael Mahemoff, has written about an experience with optimizing JavaScript from a 90 second benchmark to 3 seconds.

The hog that slowed down the system was $$(".classname") being used on a large DOM:

After some sophisticated profiling ((new Date()).getTime():D), the main culprit was revealed to be prototype’s $$. It’s a fantastic function, but if you try to grab all elements belonging to a certain class, and the DOM is really big, $$(“.cssClassName”) can be slow. *REALLY SLOW* in IE. Remedy

  • Removed trivial usages of $$() – e.g. in one case, the script was using it as a simple shorthand for a couple of DOM elements, and it was easy enough to hardcode the array. i.e.
    $$(".instruction") becomes [$(“initialInstruction”), $(“finalInstruction”)]. The former notation is cuter, but unfortunately impractical on a large web page.
  • Introduced the unofficial selector addon. Seems to have improved performance in more complex queries, i.e. $(“#knownId .message”), but doesn’t seem to have affected performance of $$(“.classname”).
  • Finally, I bit the bullet and scrapped $$(“.classname”) altogether. It’s more work, but the script now maintains the list of elements manually. Whenever an element is added or removed, the array must be adjusted. Furthermore, even the initialisation avoids using $$(), thanks to some server-side generated JS that explicitly declares the initial list of elements belonging to the class (i.e. the list that would normally be returned by $$()). To do this, the following function is called from onload(), generated with RHTML.

  1. function findAllItems() {
  2. < % js_array = { |item| "document.getElementById('item#{}'),"}.join
  3.       js_array = js_array[0..-2] if @items.length>0 # Drop extra comma at end -%>
  4.       return [< %= js_array %>];
  5. }

Read more from Michael on the subject

Posted by Dion Almaer at 8:21 am

4.1 rating from 21 votes


Comments feed TrackBack URI

It makes no sense to use $$ for such a simple selector. document.getElementsByClassName will be orders of magnitude faster, especially with the recent performance optimizations (including querying by XPath if it’s available).

That said, making $$ less costly is one of the major points of focus for Prototype’s 1.5.0 release.

Comment by Andrew Dupont — September 27, 2006

I was just about to propose using XPath if available when I read Andrews comment. So Prototype is using XPath now. That’s great. I guess I gotta dig into the code a bit and check out what other stuff has changed since 1.4.

Comment by David — September 27, 2006

Interesting Finds: September 27, 2006

Trackback by Jason Haley — September 27, 2006

Well, shouldn’t $$ then use document.getElementsByClassName or XPath itself whereever possible?

Comment by Martin — September 28, 2006

See also addListenerByClassName

Comment by Philip Tellis — September 30, 2006

Leave a comment

You must be logged in to post a comment.