One of the constant battles that we fight in the Crucible team is against the performance of web browsers when viewing reviews with ridiculously large DOM structures. I touched upon this in my previous post when I detailed how the Crucible team used event delegation and live events to work around making expensive bind calls on too many page elements.
In this post, I’d like to share two different methods of improving perceived performance for users when dealing with pages that can exhibit sluggish behaviour.

Avoid reflows

If you’ve been doing web development with any sort of performance related work, then you should already be familiar with the concept of avoiding page reflows where possible. From Steve Souders:

A reflow requires that CSS rules be reapplied, which means the browser must once again match all the CSS selectors. If the CSS selectors are inefficient, the reflow may take a long time – long enough that the users notice.

In this quote, Souders talks about the effect of reflow as a measure of how efficient it is to evaluate the selectors. What isn’t mentioned, is that reflow times is also heavily dependent on the number of elements on the page to apply those style rules on; i.e. the dom size.
There are usually plenty of actions that cause reflow on a page, but it can sometimes take a little bit of ingenuity to try and reduce how often they are triggered.
If you’ve never seen it before, here’s a great video which shows the paint actions which occur when loading Wikipedia:

UI Ghosting

Changing the dimensions of elements obviously cause reflows. The page structure in a typical Crucible review looks something like the following figure:
The right hand pane contains the majority of the document structure (80% or so). The left hand pane can be resized which causes the dimensions of both panes to change, which causes browsers to reflow content.
Imagine resizing your browser window. As you drag the resize handle, the window smoothly resizes and updates it’s geometry and contents in close to real time. This is desired in most cases, except when the action of calculating and repainting the contents is lengthier than the time between resize events (or possibly too expensive to justify).
In earlier releases of Crucible, resizing the splitter between this content would trigger immediate content refreshes. This turned out to be sub optimal on large reviews since that reflow operation could take a signification amount of time (hundreds of milliseconds!). We worked around this with a neat trick: ui ghosting. By overlaying a duplicate element with an opacity, we can achieve a great effect of showing the resize operation without causing a reflow until the user has decided what the final size should be. Check it out:
If you use jQuery UI, you can easily achieve this effect with the ghosting option of resizable widgets.

On demand content fill

Here’s another, more drastic change. If you have a problem with resizing due to a large dom, chances are that most of the tree is out of the visible viewport. That means that you can safely remove content from the dom if you’re not showing it and you don’t actively need it. If you’re worried about scrollbars being all over the place, then as long as you know the actual height of the content you can fake it with css styles. As the user scrolls the page, you can actively insert and remove content based on what is visible (plus a bit more for good measure). Request the content via Ajax, or load the content into JavaScript for easy access. The basic principal looks as follows:
Delayed content loading
With this method, the page will feel snappier almost always, since there is much less content in the page. We use this strategy very effectively in our new side-by-side diffs amongst other places.
Hopefully this blogs reminds you to keep thinking about how you can improve your user experience when you have large pages and improving performance is difficult. Try crazy things, wade out into unchartered waters and tempt fate by pushing browsers to their limits.

Marrying User Experience and Performance