Visits to this blog

Dojo gets a speed boost on IE6 and IE7

My former employer, Cúram Software, was good enough to give me a couple of months to work on the performance issues that Dojo and it’s widget project Dijit have in horrible legacy browsers IE6 and IE7. Some of the fruits of that labour have now been checked into the code base and will be included in Dojo 1.6, out in Q4 2010.

So what I/we learned from the exercise?

DynaTrace rocks!

To begin with, I can’t praise DynaTrace enough. It’s a fantastic piece of profiling software that delves deep into Internet Explorer’s innards to find every little horrible Microsoft bug. It can take a while to get used to, but that is just because it presents you with so much data, all of it useful, and anyone would have trouble finding a way to make it easily consumable. Long story short, if you’re having performance issues with your site in IE, download DynaTrace.

Touching className makes IE cry

Pretty much every time you change the className property of a node, IE6 and IE7 reflow the page. It doesn’t matter if the new value is the same as the old one, or whether or not the changed CSS class has any visual effect, IE will redraw the page, resulting in a lot of time being lost.

Dojo was changing className too often, for example in dojo.addClass, if the class being added was already present, it would still assign the changed value to className. I put a fix for this into v1.5. There were also many cases where Dojo would use a combination of dojo.removeClass and dojo.addClass to replace a class, which changed className twice instead of just once. To fix this, I’ve added a new public api dojo.replaceClass, which only changes className once.

Dijit widgets often add CSS classes to the DOM nodes which they are creating after the nodes have been inserted into the document. This is costly, and a large performance improvement can be gained by applying these classes earlier in the lifecycle, before inserting into the document. Bill Keese (Dijit master) checked in the first of the fixes for this for the BorderContainer recently, and a strategy is being formulated for the many other widgets that can benefit from this. This work should be completed for v1.6.

Risky but…..

A solution that we cannot use for Dojo, but which we found gave huge performance gains for my company’s application, was to figure out what CSS classes Dojo would apply to a node and write those classes out when generating the HTML. For example, when creating a dijit.layout.BorderContainer, our HTML contains the CSS class dijitBorderContainer. There is an inherent risk with this approach, as Dojo may change the CSS classes in a later release, but if you’re willing to accept the risk and the upgrade pain, you can get a nice performance boost this way.

Reading offsetLeft and offsetTop is costly

When your code reads the offsetLeft or offsetTop of a node, all pending changes to the DOM have to be performed first. This can be very expensive – we saw a number of single operations taking over 500ms each! Many Dijits were using the dojo.marginBox method to get the left, top, width and height properties of a node, when all they needed was the width and height.

To solve this, I introduced a new method, dojo._getMarginSize, a private method that just returns the width and height of a node. This has resulted in huge gains in performance on IE6 & 7, especially when used with layout widgets like the BorderContainer.

Unexplained gaps in DynaTrace profiles

We hit a bit of a wall at one point when analysing the performance profiles, where there was a 1.5 second gap in the profile where it seemed like nothing was happening, but everything was paused with the CPU at 100%.

We eventually discovered that there is a horrible bug in IE7 where if you have a :hover style on something that is not an anchor tag, it causes the rendering engine to go crazy, get all confused, and push the CPU up to 100% for no reason at all.

The solution: don’t user :hover styles on anything other than <a> tags. Ridiculous I know, but so is the IE7 rendering engine.

Give it a go!

So, the long and short of it is that Dojo/Dijit should now be a good bit snappier on IE6 and 7. With our application we got the load time for a huge application down from 20 seconds to 5 seconds using these techniques, making IE7 far, far more usable. Of course it just flies on any other browser, even IE8 which, all credit to MS for once, is far less buggy than the earlier incarnations.

So feel free to try out the Dojo nightlies, you should find them to be a good bit more responsive in IE6 and 7 than just a few days ago, and now that we have learned some important lessons, Dojo will be even faster for v1.6.

Great to see dojo and dijit getting even more speed improvements for the 1.6 release. I just upgraded from 1.3 to 1.5 recently and definitely found my application that little bit snappier. Upgrading to 1.6 looks like an attractive proposition with blog posts like this. Had a few questions related to the post:

1. I usually use dojo.addClass/dojo.removeClass in a postCreate method of custom dijits. Should I consider moving this code to a different dijit lifecycle method to save the number of reflows caused by addClass/removeClass? In some of the 1.6 changesets, I see some addClass/removeClass functionality being switched around from postCreate to buildRendering. Does buildRendering fire before the widget is added to the entire document?

2. Will dojo._getMarginSize be made ‘public’? I’ve plenty of places in my code where this function could be used, but I’ve always tried to stay away from ‘private’ api entrypoints.

1. Yes, move that to either buildRendering or postMixInProperties if you can, as they both occur before the templated node is inserted into the DOM. However, if you are not replacing the original DOM node (if you’re creating widgets declaratively), then the node is already in the DOM, so leave the code in postCreate.

Thanks for clarifying those few points for me – much appreciated. Quite a few of my widgets are instantiated via declarative markup, so I reckon I could gain some performance karma by moving addClass/removeClass to buildRendering/postMixInProperties.

Many thanks for this new compatibility effort.
Thus after modifying your Feature Explorer in my local disk, the local Dojo Feature Explorer seems run correctly except dropdown list containing json data of entire samples hierarchy.

The “search” (Div widget_searchCombo and jscript and sub Div) have 3 lines on local (1 on Dojo site).
The 1st line is yellow icon with exclamation point with at right the down arrow
The second the letter X under yellon icon
The third the text box

I believe, this part of code is generated at display but have no idea to complete.
If this part of code (Search) is deleted, the loop to scan data.js doesnt work and we have no menu…

Could you send me original file?
I add two dropdonw list (Languages and templates) to full customize and send you this new one after testing (on Dojo 1.5).

Steven Woodsaid

Thanks for these great tips. I had a question about using dojo.marginBox…. supposing i only want the width, then asking for the offsetWidth in my script causes the browser to execute all pending DOM changes (since it has to reflow in order to work out how wide the element is). Assuming this is correct, if you then ask for offsetTop immediately after asking for offsetWidth, does IE reflow again ? yikes… in the short term, should we just use offsetWidth directly instead of dojo.marginBox(node).h ? Until we upgrade of course…

If you get the offsetWidth of a node, and immediately get offsetTop, IE doesn’t reflow a second time. The problem comes when you interleave code. E.g. if you get offsetWidth, then set the height of the node, then get it’s offsetTop, it will reflow twice.

Other browsers solve this by not reflowing at all until the currently executing Javascript command has completed

I don’t know if it’s just me or if everybody else experiencing problems with your blog.

It appears as though some of the text within your content are running off
the screen. Can somebody else please provide feedback and let me know if this is happening
to them too? This may be a issue with my browser because I’ve had this happen before.
Cheers