views:

485

answers:

3

Hello,

I have a javascript function that changes the css display ('block', 'none') of a large number of dom nodes (>5000). I am trying to make this as fast as possible.

Will IE interrupt javascript to reflow and paint the screen, or will it wait until the javascript engine returns control to the browser? I want to avoid any extra work from the browser until all nodes have had their display set.

I noticed in JQuery's show() method, DOM operations are put in a tight loop with the comment

// Set the display of the elements in a second loop
// to avoid the constant reflow

(http://code.google.com/p/jqueryjs/source/browse/trunk/jquery/src/fx.js line 51)

Thanks,

James

A: 

If you hid a common parent, and then hid the elements, and then showed the parent, that should keep it from repainting unnecesarrily.

Allen
Would the user see a flicker? Will the browser not paint until javascript is finished running?
James
I ran some tests and this seems to make performance worse. Chrome went 29ms->30ms, IE8 432ms->493
James
A: 

The browsers will reflow the page as the JS changes each element. As you are suggesting, it is best to keep the changing of the display until the end. This will also feel smoother and faster (even if it isn't really) to the user.

I would:

  1. Clone a node that wraps the nodes that you want to process.
  2. Do the processing on the cloned nodes children.
  3. Replace the original node with the clone.

If the processing takes a long time (more than 0.5 of a second) consider setting something on the display to let the user know that something is happening. Slowly fading out the original content to a minimum of a about 50% opacity is a simple way.

edeverett
Perhaps I'm misunderstanding what you wrote, but the browser _will not_ make visual changes while JavaScript is executing. JS must relinquish control. That's why you have to use SetTimeout() with any JS that goes on for too long. In most (if not all) browsers, even animated GIFs freeze if JS doesn't return control to the browser.
Nosredna
Hmm, ok, maybe I'm getting myself confused. Thanks for the correction
edeverett
+2  A: 

Use CSS to show and hide the elements instead, by changing the class name of a parent element. Some tests I did a while back showed that it's about ten times faster than setting the display property on each element. As you are only changing a single attribute, it only reflows once.

Example:

<style>
.State1 .InitiallyHidden { display: none; }
.State2 .InitiallyShown { display: none; }
</style>

<script>
function flip() {
   var o = document.getElementById('Parent');
   o.className = o.className = 'State1' ? 'State2' : 'State1';
}
</script>

<input type="button" value="flip" onclick="flip();" />

<div id="Parent" class="State1">
   <div class="InitiallyHidden">One</div>
   <div class="InitiallyShown">Two</div>
   <div class="InitiallyHidden">Three</div>
   <div class="InitiallyShown">Four</div>
</div>
Guffa
This is the correct way to do it. If you find yourself changing 5000 nodes you're doing something wrong.
mwilcox