When jQuery 1.4 came out last week I just finished a large project involving lots of ajax, XML-parsing and DOM-manipulation. In this backoffice-tool large tables replace other objects in the page and then events are being bound to cells, icons in the cells for context-menus …
What was reasonable fast with my test-data became a sleeping snail after the customer started working with it, adding some hundred lines of real data.
I figured out there were two extreme time-expensive things in what I was doing there:
- injecting new elements in the site
- eventbinding to lots of DOM-objects
The later was fixed easily and (as I think) nifty – I will talk about that in another post. The DOM-injection was a problem because jQuery is doing some clever things when deleting elements from the DOM to prevent memory-leaking. So simply using .innerHTML= instead of .html() was an option for me, but not one i liked.
So there was some excitement when I figured out that .empty() and .html() were among the things that had been speed up in jQuery 1.4.
I set up a testpage for my scenario (adding, removing and binding events to large tables) comparing jQuery 1.3.2 with 1.4 in Firefox 3.5.5 and see what I’ve got:
the 11 Testcases
I implemented 11 testcases I wanted to compare:
- Adding a large table to a empty container using $(‘#container’).html(table);
setting up the string with the table (400 rows, 30 columns) is never counted
- adding the same table again to the same container – replacing the first table
- using .empty() on the container – deleting the table
- adding the table again
- adding a class to every cell using $(‘#container td’).addClass(‘class’);
- adding a 2nd class using $(‘#container td’).addClass(‘class2′);
- removing one of the two with $(‘#container td’).removeClass(‘class’);
- setting a class with $(‘#container td’).attr(‘class’, ‘new’);
- changing the content of every cell with $(‘#container td’).html( ‘new’);
- binding a click-event to all cells $(‘#container td’).click(clickevent);
- removing the table with all the bound events using $(‘#container’).html(”);
The absolute Numbers don’t matter. They depend on the system, what windows is doing in the background … the numbers fluctuate from test to test by 10 to 25%. Nevertheless there are some things obvious:
- jQuery 1.4 is faster – especial .empty(), .html(), addClass() and removeClass() are noticeable quick now
- adding content to a container containing DOM-elements is slower then adding it to an empty container
- removing elemts with bound events was extremely slow in jQuery 1.3.2 (and the former versions)
The surprise for me was how fast .empty is now. With lots of events on elements it took a long time to get rid of them. Now it’s almost as fast as using .innerHTML= not caring about memory-leaks. Since .html() uses .empty() its clear why the former is faster now.
Let’s have a look at the results in Internet Explorer 6 and 8
The ugly green beast is IE6, in blue you see version 8.
I cut of the .attr() – results for IE6 because they where making the chart unreadable. Values: 33.17 seconds in 1.3.2 and 29.3 seconds in 1.4!
The good things first: IE 8 is faster than 6 and 1.4 is faster than 1.3.2 in IE as well.
While .empty is a lot faster now in both Redmond-browsers I ask myself what these old browsers are that need a special .empty() – routine to work around memory-leaks. Maybe its IE 5.5 – but I couldn’t care less if thats the fact.
As promised class-manipulation is a lot faster with jQuery 1.4 taking half the time in v6 and v8. What we learn here is that you shouldn’t use .attr() if you can work around it. IE8 is slow there and IE6 is “veeeerrrryyy slow” – being 6 times slower than IE8 and 20 times slower compared to Firefox. (browser-comparison further down the post)
The last test subject is Google Chrome 4.
The speedup with jQuery 1.4 is obvious in Chrome as in all other browsers I tested. .empty() works like a charm now an class-manipulation is faster as well.
I’d like to call the decrease in some testcases as in the “measuring tolerance” – but they accured in 3 test-runs. I’m not sure whats happening there, but Chrome is slower with jQuery 1.4 on a high level – still outperforming the other browsers.
Which brings me to
All Results are for jQuery 1.4 because we know it’s faster than older versions.
- As you can see – IE 8 is slow and IE 6 is slower – we know this already. Manipulation of attributes is very expensive there and it’s a good advice to work around using this if you expect any IE-users to work with your site or web 2.0 application.
- WebKit-Engine is the fastest taking only half the time Firefox needs (all values summed together). Manipulations of large groups of elements such as the addClass(), attr() or .html() to all 12000 cells are very fast there. This makes a WebKit-browser a good suggestion if you can get your customer to follow your suggestions about software he/she should use. If Google Chrome is a good advice is another story, maybe Safary is worth a try.
- Firefox is fast enough but could use some speedup when it comes to manipulating the content of lots of objects.
- Binding events to lots of elements takes some time – I didn’t test .live() here – thats subject for a new post. I guess using .live() will slow down the injection of the elements in the DOM.
- Don’t use .attr() unless you really have to. And don’t you dare using it to set a class to an element that you know has none. (Not talking about some fools still using attributes to store information instead of using .data())
How this was done
Some notes about how I collected these numbers.
My first idea was using firebug with console.time(‘test1′); but i figured out that firebug is slowing everything down. Not proportional as you might think but depending on what you are doing. Binding events with firebug open takes ages – like 12 seconds compared to 1 second with firebug open.
So i did the flowing: (shortened)
var time = new Date();
var s = t();
Where #out is a container in my testpage holding all results.
So one advice I can give you (and thats another lesson I learned) is not to trust firebug to much when it comes to profiling your webapp. Its a good tool for development but when it comes to finding bottlenecks in your code it might be a good idea to disable it from time to time and see how everything performs without it.
If you have anything to add here – some additional knowledge or you find something is wrong at all – let me know – write a note.
Last: if you want to take the test on your system: feel free to do so .Its free to try, change redistribute or whatever. If you use and write about it – add a link to my page.