views:

262

answers:

2

I am building the diagram component in JS. It has two layers rendered separately: foreground and background. In order to determine the required size of the background:

  1. render the foreground
  2. measure the height of the result
  3. render the foreground and the background together

In code it looks like this:

var foreground = renderForegroundIntoString();
parentDiv.innerHTML = foreground;
var height = parentDiv.children[0].clientHeight;
var background = renderBackgroundIntoString(height);
parentDiv.innerHTML = foreground + background;

Using IE7, this is a piece of cake. However, Firefox2 is not really willing to render the parentDiv.innerHTML right away, therefore I cannot read out the foreground height. Could someone enlighten me on when does Firefox execute the rendering and how can I delay my background generation till foreground rendering is completed, or any alternative way to determine the height of my foreground elements?

[Appended after testing Dan's answer (thanx Dan)]

Within the body of the callback method (called back by setTimeout(...)) I can see, the rendering of the innerHTML is still not complete.

+2  A: 

You should never, ever rely on something you just inserted into the DOM being rendered by the next line of code. All browsers will group these changes together to some degree, and it can be tricky to work out when and why.

The best way to deal with it is to execute the second part in response to some kind of event. Though it doesn't look like there's a good one you can use in that situation, so failing that, you can trigger the second part with:

setTimeout(renderBackground, 0)

That will ensure the current thread is completed before the second part of the code is executed.

Dan
A: 

I don't think you want parentDiv.children[0] (children is not a valid property in FF3 anyway), instead you want parentDiv.childNodes[0], but note that this includes text nodes that may have no height. You could try looping waiting for parentDiv's descendants to be rendered like so:

function getRenderedHeight(parentDiv) {
if (parentDiv.childNodes) {
  var i = 0;
  while (parentDiv.childNodes[i].nodeType == 3) { i++; }
  //Now parentDiv.childNodes[i] is your first non-text child
  return parentDiv.childNodes[i].clientHeight;
  //your other code here ...
} else {
  setTimeout("isRendered("+parentDiv+")",200);
}
}

and then invoke by: getRenderedHeight(parentDiv) after setting the innerHTML.

Hope that gives some ideas, anyway.

Eric Wendelin