Your Javascript is Slow
And I’m not saying that because you’re using Internet Explorer. Chances are pretty good that if you’re a Rails developer, your Javascript skills are not your strongest, rather your Ruby skills are. And you use Firefox or Safari while developing, so things seem fast enough to you. Its going to be okay though, I’ll give you a few pointers to speed it up.
You probably learned a lot of what you know about Javascript and how to write it through various examples on the blogs of your favorite Rails gurus, or maybe by copying examples from prototypejs.org.
The problem with a lot of Javascript examples and snippets on the Internet are that they work fine for “proof of concept” samples with a dozen objects, but they fall flat on their face performance-wise once the code is in a real application with hundreds or thousands of elements. Frequently this is a result of the code never being run with a sizeable amount of data. Also keep in mind that the code you’ll find on blogs was written in a, “look what I just did,” moment and usually does not include the, “oh crap, sorry folks!” feedback after being put in production.
The Prototype.js library is a great library that has opened a lot of doors, but it can be very easy to write slow Javascript with it. The problem isn’t the library itself, rather its how easy it makes it to write complicated code (kind of like Rails and SQL queries!). You may start off with an example you found that iterates over some DOM elements and passes them to a Class to get instantiated (having a “good” Object Oriented Design). Then you later come back to the code and add a few more things inside the loop and to the class, and before you know what happened, IE users are calling support complaining about their browser freezing for 60s after a page load!
So here is a basic skeleton of something you might see:
// Create a nice self-contained class for our menu behaviors var SlowMenu = Class.create({ initialize: function(menu_el){ this.menu = menu_el; this.menu.observe('click', function(){//click behavior}); } }); // Add the behaviors to all DOM elements var flyouts = $$('li.flyout_menu').map(function(el){ return new SlowMenu(el); });
A project I worked on had a user directory with hundreds of elements on the page that the Javascript touched. Just a simple decision such as creating a new object with attached behaviors for each element brought the browser to a crawl. Speeding it up required leveraging Javascript’s functional nature and keeping the design going with the grain of Javascript’s out-of-the-box features.
It is pretty common to say, “Ok, I need a flyout menu… I’m going to make a class that contains the menu behaviors, then grab all the links on the page and make new objects with them.” This results in Javascript fail. In the example above, if you decide to show 100 menus on the page, and each flyout menu has, say, a dozen links in it that all need behaviors, you’re up to thousands of objects for simple functionality!
A seemingly better way is to have one loop that just attaches one behavior to each element. That way you’re at least not instantiating new classes inside the loop, right?
$$('li.flyout_menu').each(function(el){ el.observe('click', function(){//click behavior}) });
Actually, the best way is to not even mess with the elements themselves! Instead just let any events you’re interested in bubble up the DOM and only make one top-level function that knows what to do by inspecting the target of an event.
$('menu_container').observe('click', function(event){ if (event.element().hasClassName('flyout_menu')) { // Menu behaviors go here. } });
This sample code only observes click events on the parent element, and inspects the incoming elements on the fly. Instead of performing all of the necessary computation when the Javascript is first executed by the browser, this will delay any computation until the last possible moment the user needs it. This will make page loads feel snappy for the user, and save their CPU if they’re unfortunate enough to be using IE. Your browser is already downloading and interpreting the script so you want to avoid doing any more work while this is being done.
These design choices don’t apply to just Prototype.js. This style is just as effective when using jQuery. Here is how the above snippet would look using jQuery:
$('#menu_container').click(function(event){ if ($(event.target).hasClass('flyout_menu')) { // Menu behaviors here } });
So when you’re faced with some slow Javascript, chance are that you’ll deny it. I responded that way the first few times this happened to me. I use Firefox for most of my development, and the latest Firefoxes (and Safaris, and now Chrome) have really fast Javascript interpreters. You may not even notice a delay, whereas someone running IE will experience the same code taking a second or more to run.
While developing and benchmarking Javascript, I’ve found IE to generally be 10-100x slower than Firefox running the same code. Make sure to open up VirtualBox and feel the pain of IE every once in awhile when developing.
Ultimately, the best way to speed up your Javascript is to write less Javascript! A majority of the time you don’t need classes and inheritance in your Javascript code like you are used to with languages that have more traditional object systems. Keep in mind first of all that events bubble up the DOM (if you’re using a library), and consider turning your code’s design inside-out. If you’re attaching the same behavior to every element, try instead to have one instance of the behavior that can be parameterized with an element.
The other thing to remember is that modern applications dealing with thousands and millions of objects are going to be running on IE6 (or IE7 if you’re sane) so try give those users a decent enough experience. Though if you can afford to tell them to take a hike, have a beer for me!
Our guest blogger series recognizes talented developers who’ve been around the block or two and offer practical advice on today’s Web dev challenges. They are also available for hire as part of the Webficient project team. Contact Webficient Now
About this entry
Posted: Monday, May 4th, 2009 at 10:32 pm
- Author:
- Dave Woodward
- Category:
- Solutions
- Tags:
- guest blogger, javascript, jquery, performance, prototype
- License:
- Creative Commons

5 Comments
Jump to comment form | comments rss | trackback uri