3

I often read that changing a style after reading the element's style is a bad practice as it triggers an unnecessary reflow. Consider this code from here:

Bad Code:

elementA.className = "a-style";
var heightA = elementA.offsetHeight;  // layout is needed
elementB.className = "b-style";       // invalidates the layout
var heightB = elementB.offsetHeight;  // layout is needed again

Good code:

elementA.className = "a-style";
elementB.className = "b-style";
var heightA = elementA.offsetHeight;   // layout is needed and calculated
var heightB = elementB.offsetHeight;   // layout is up-to-date (no work)

I am curious to know why elementA.offsetHeight will cause a layout? Here we are simply reading already applied changes, not really applying a change (like in case of elementA.className = "a-style").

darKnight
  • 5,651
  • 13
  • 47
  • 87
  • It's likely due to deferred rendering, if you read a property you intercept the deferred part. So will be slower. – Keith Feb 23 '20 at 12:13

1 Answers1

3

Here we are simply reading already applied changes...

Not really. Assigning to className means the browser has to reflow, but it doesn't mean it already has reflowed when you're done assigning. It may wait (in modern browsers, will wait) until its next paint, which won't happen until after the current JavaScript code completes (at least).

But when you read a calculated property like clientHeight, the browser has to pause the JavaScript code and reflow the page so it can return an accurate number. So your "good" code does this:

elementA.className = "a-style";        // marks the layout stale
elementB.className = "b-style";        // marks the layout stale (no change)
var heightA = elementA.offsetHeight;   // triggers reflow
var heightB = elementB.offsetHeight;   // no need for reflow, the layout isn't stale
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Interesting. But what if there are no style changes like assignment of a `class` involved? Will reading `offsetHeight` still cause reflow? – darKnight Feb 23 '20 at 12:28
  • @maverick - If the layout has already been calculated and is still up to date, as far as I know it doesn't. I should probably delete this answer, though. I can't cite any resources for it and I'm not an expert on reflow dynamics. I think the above is correct, but I'm not sure "I think" is good enough for an SO answer, particularly not one from someone with a high rep due to *other* expertise, which could be misleading... :-) – T.J. Crowder Feb 23 '20 at 12:31
  • 1
    I think your answer makes a lot of sense, so its better not to delete :). Even I thought of asynchronous rendering as the culprit when I was posting this question, so "I think" this is the right answer! Let's see if someone can link any valid resources here.. – darKnight Feb 23 '20 at 12:35
  • 1
    @maverick - While the advice and this answer are broadly right, I suspect there may be a fair amount of devil in the detail. As well as the question you pose about DOM changes that don't modify the render tree, there's also a question mark over changes that only impact localised parts of the layout, and therefore may only cause partial reflows. This stuff is very implementation dependant and very subject to change as CSS progresses. – Alohci Feb 23 '20 at 15:19
  • maverick, I second what @Alohci said. :-) – T.J. Crowder Feb 23 '20 at 16:12
  • 1
    As someone who wrote a bit on this subject matter, yes this answer is mostly correct. A few points of precision: Current Chrome and FF will wait the next [rendering](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model:fully-active-9) step before triggering layout if not forced, while Safari seems to do it at first idle time. For the comments' question about unchanged tree-model, that will indeed be implementation dependent, I don't think no specs have anything to say about that apart from being accurate when one of the reflow triggerers is called. – Kaiido Feb 25 '20 at 09:45
  • I suspect most browsers do have dirty flags allowing to early-exit when nothing changed (for instance if the element or one of its ancestors has `display:none`). But that's really optimizations quirks. [A related question](https://stackoverflow.com/questions/50789614/what-exactly-updates-when-the-dom-is-manipulated/50791220#50791220). – Kaiido Feb 25 '20 at 09:47