When does reflow happen in a DOM environment? - javascript

What kinds of activities will trigger reflow of web page with DOM?
It seems there are different points of view. According to http://www.nczonline.net/blog/2009/02/03/speed-up-your-javascript-part-4/, it happens
When you add or remove a DOM node.
When you apply a style dynamically (such as element.style.width="10px").
When you retrieve a measurement that must be calculated, such as accessing offsetWidth, clientHeight, or any computed CSS value (via getComputedStyle() in DOM-compliant browsers or currentStyle in IE).
However, according to http://dev.opera.com/articles/view/efficient-javascript/?page=3, taking measurement triggers reflow only when there is already reflow action queued.
Does anybody have any more ideas?

Both articles are correct.
One can safely assume that whenever you're doing something that could reasonably require the dimensions of elements in the DOM be calculated that you will trigger reflow.
In addition, as far as I can tell, both articles say the same thing.
The first article says reflow happens when:
When you retrieve a measurement that must be calculated, such as accessing offsetWidth, clientHeight, or any computed CSS value (via getComputedStyle() in DOM-compliant browsers or currentStyle in IE), while DOM changes are queued up to be made.
The second article states:
As stated earlier, the browser may cache several changes for you, and reflow only once when those changes have all been made. However, note that taking measurements of the element will force it to reflow, so that the measurements will be correct. The changes may or may not not be visibly repainted, but the reflow itself still has to happen behind the scenes.
This effect is created when measurements are taken using properties like offsetWidth, or using methods like getComputedStyle. Even if the numbers are not used, simply using either of these while the browser is still caching changes, will be enough to trigger the hidden reflow. If these measurements are taken repeatedly, you should consider taking them just once, and storing the result, which can then be used later.
I take this to mean the same thing they said earlier. Opera will try its hardest to cache values and avoid reflow for you, but you shouldn't rely on its ability to do so.
For all intents and purposes just believe what they both say when they say that all three types of interactions can cause reflow.
Cheers.

Look at the "Rendering triggered by Property Read Access" section of Understanding Internet Explorer Rendering Behaviour, where the following code in IE will cause rendering activity.
function askforHeight () {
$("#lower").height();
}

document.body.style.display = 'none';
document.body.style.display = 'block';
This often solves those incomprehensible layout bugs.

Related

When exactly does the browser repaint and reflow?

I was trying to understand better what happens when DOM manipulations happen in a browser. Something that I could not find a lot of resources about, was when exactly repaints and reflows are executed by the browser (there is a lot of literature what a repaint or reflow is, but almost nothing about when it happens).
Very specifically, I was wondering about code like this:
function clickHandler() {
domElement1.textContent = 'Hello';
domElement2.textContent = 'World';
}
Assume that there are two DOM elements stored in the variables domElement1 and domElement2. The clickHandler function is attached to a button, and should update the content of both DOM elements when it is clicked.
Now the question: Is it possible that this causes two repaints? I.e. that the browser updates only domElement1, shows that change on the screen causing repaints/reflows/etc., and afterwards updates domElement2 causing another repaint/reflow/etc.? Or is there a guarantee that the event handler of the button will be completely executed before anything is changed in the actual DOM?
My guess would have been that the browser tries to keep 60 frames per seconds, and that theoretically it could happen that these two changes might happen in two different frames. Is that correct?
Browser reflow and browser repaint are two different, but related, ideas.
Reflow is essentially the computation of the offsets and dimensions of given elements to understand what comes where and takes how much of space, and so on.
Repaint is the next stage after reflow. It is when the computation made in the reflow stage is actually used to paint pixels on the screen. This process is sometimes also known as rasterization.
First thing's first: repaint only happens when the main thread is free, i.e. when no event listener, no callback, just simply nothing is currently in line for execution. And, as I can make its intuition, repaint only happens when a reflow was triggered previously.
In contrast to this, a reflow might be triggered with every single DOM mutation, depending on the underlying code.
As you can reason, the most naive approach for any browser is to trigger reflow after every single DOM mutation (e.g. the domElement1.textContent = 'Hello'; statement that you shared above). But as you might agree, this would be extremely inefficient.
What if there is a whole bunch of such statements in a line? Clearly, in this case, it would be much much better for the browser to do reflow just once at the very end of the whole block of statements and thus keep itself from wasting computing resources.
Now here's one thing to keep in mind. As you may know, browser engines, especially for JavaScript, have become extremely complicated, sophisticated and intelligent over the past few years. These days, they can make extremely intelligent guesses on what a code precisely asks for and then ultimately apply numerous kinds of optimizations over it in order to run it at the top-most speed.
These optimizations include holding on to the execution of the reflow algorithm when there is no need for it. That how every single browser exactly does this is out of the scope of this answer and is obviously implementation-dependent. Chrome might rely on one method, Firefox on the other, Safari on the other, and so on.
There isn't really any point in digging into each one's code bases and finding the exact way it works.
However, there is a common thing between all browsers these days and that is that they try to do the least amount of unnecessary work. That's one of the reasons of the success of modern-day JavaScript — the robust engines are right at its back.
So, to end this answer, I would say that no, there is almost no possibility that the following code would trigger reflow twice:
function clickHandler() {
domElement1.textContent = 'Hello';
domElement2.textContent = 'World';
// Reflow should happen at the end of this function.
}
However, the following might trigger it twice because the second statement wants to know about something (i.e. the distance of domElement1 from the top edge of the viewport) that can only be obtained once the reflow algorithm has been run by the browser:
function clickHandler() {
domElement1.textContent = 'Hello';
console.log(dom1Element.getBoundingClientRect().top); // Should trigger reflow.
domElement2.textContent = 'World';
// Reflow should happen at the end of this function.
}
Note the usage of the words 'almost' and 'might' in the sentences above. I've used them to emphasize on the fact that I am not 100% sure. There is over 50% probability that what I say does actually happen on every single browser, but the probability is still not 100%. As I said before, engines work differently, and I can't tell you something blindly which I've never inspected myself.

How React.js speeds up rendering with a virtual DOM

Quoting this (https://news.ycombinator.com/item?id=9155564) article
The short answer is that the DOM is not slow. Adding & removing a DOM
node is a few pointer swaps, not much more than setting a property on
the JS object.
Are the DOM bottlenecks only those things that cause a redraw? If so then shouldn't one render from React's virtual DOM amortize to the same performance as redrawing an entire component (in one browser API call of course)? I would think that the algorithms executed by the browser only try and redraw the diff from one state to another (like git maybe?). Implying that the browser maintains a virtual DOM by itself. So then what is the point of having a virtual DOM?
Also should adding an element that has the display style property set to none not be affecting performance badly? I would profile this myself but I do not know where exactly to turn as I started javascript programming only recently.
This question may be somewhat broad for SO, but as a general answer, some other quotes from the same article are also very relevant:
However, layout is slow...
[...]
Worse, layout is triggered synchronously by accessing certain properties...
[...]
Because of this, a lot of Angular and JQuery code is stupidly slow
[...]
React doesn't help speed up layout...
What react's virtual DOM does, is calculate differences between one state of the DOM and the next state and minimizes DOM updates in a very smart way.
So:
DOM itself is not slow
but layout is slow
and almost all DOM updates require layout updates
so less DOM updates is faster
And the react engine does just that (same as several other tools/ libraries with a virtual DOM).
More info on what virtual DOM is and its advantages e.g. here.
Q: "Are the DOM bottlenecks only those things that cause a redraw?"
A:
The redraw is GPU dependent. Has nothing to do with the speed od DOM updates. DOM updates are almost instant.
Everything depends on changes that do affect the document flow. If a certain DOM or DHTML change affects the document flow. The closer the affected element is to the root of the document element the greater the impact on the document reflow.
You don't need to change the DOM content in order to cause a document reflow. A simple style property change on a given parameter may push elements of the stream to change position and cause therefore force the document reflow.
Therefore no, DOM changes on fixed size Elements will not cause a document reflow, whereas the update of display is practically instant. Will be applied only on locally affected area, most of the time in a frame which may be less than 300 x 200 pixels square; a size of an area that can be redrawn with over 120fps on a really slow GPU's. But that's 5 times smoother than watching Avengers in Cinema.
( Any spatially nonequivalent change in stream-aligned content will cause a reflow. So we have to watch for changes that affect the size and position of our floating elements, changes on inline elements inside a long stream of another inline element, etc, etc. )
'
Q: "should adding an element that has the display style property set to none not be affecting performance badly?"
A:
That's correct. Adding an element with style.display: "none" to the DOM will cause no change to the existing rendering of the document, and therefore, not trigger a document reflow and will, naturally, have no impact at all; i.e.: will be as fast as adding a new property to a JavaScript object.
Regards.

Reflow/Layout performance for large application

I am using GWT to build a HTML application where the performance is correct in general.
Sometimes, it can load many objects in the DOM and the application becomes slow. I used Chrome Developer Tools Profiler to see where that time was spent (under Chrome once the app is compiled ie no GWT overhead) and it is clear that the methods getAbsoluteLeft()/getBoundingClientRect() consume the major part of this time.
Here is the implementation used under Chrome (com.google.gwt.dom.client.DOMImplStandardBase) :
private static native ClientRect getBoundingClientRect(Element element) /*-{
return element.getBoundingClientRect && element.getBoundingClientRect();
}-*/;
#Override
public int getAbsoluteLeft(Element elem) {
ClientRect rect = getBoundingClientRect(elem);
return rect != null ? rect.getLeft()
+ elem.getOwnerDocument().getBody().getScrollLeft()
: getAbsoluteLeftUsingOffsets(elem);
}
This makes sense to me, as the more elements in the DOM, the more time it may take to calculate absolute positions. But it is frustrating because sometimes you know just a subpart of your application has changed whereas those methods will still take time to calculate absolute positioning, probably because it unnecessarily recheck a whole bunch of DOM elements. My question is not necessarily GWT oriented as it is a browser/javascript related problem :
Is there any known solution to improve GWT getAbsoluteLeft/javascript getBoundingClientRect problem for large DOM elements application ?
I did not find any clues on the internet, but I thought about solution like :
(reducing number of calls for those methods :-) ...
isolate part of the DOM through iframe, in order to reduce the number of elements the browser has to evaluate to get an absolute position (although it would make difficult components to communicate ...)
in the same idea, there might be some css property (overflow, position ?) or some html element (like iframe) which tell the browser to skip a whole part of the dom or simply help the browser to get absolute position faster
EDIT :
Using Chrome TimeLine debugger, and doing a specific action while there are a lot of elements in the DOM, I have the average performance :
Recalculate style : nearly zero
Paint : nearly 1 ms
Layout : nearly 900ms
Layout takes 900ms through the getBoundingClientRect method. This page list all the methods triggering layout in WebKit, including getBoundingClientRect ...
As I have many elements in the dom that are not impacted by my action, I assume layout is doing recalculation in the whole DOM whereas paint is able through css property/DOM tree to narrow its scope (I can see it through MozAfterPaintEvent in firebug for example).
Except grouping and calling less the methods that trigger layout, any clues on how to reduce the time for layout ?
Some related articles :
Minimizing browser reflow
I finally solve my problem : getBoundingClientRect was triggering a whole layout event in the application, which was taking many times through heavy CSS rules.
In fact, layout time is not directly proportional to the number of elements in the DOM. You could draw hundred thousands of them with light style and layout will take only 2ms.
In my case, I had two CSS selectors and a background image which were matching hundred thousands of DOM elements, and that was consuming a huge amount of time during layout. By simply removing those CSS rules, I reduce the layout time from 900ms to 2ms.
The most basic answer to your question is to use lazy evaluation, also called delayed evaluation. The principle is that you only evaluate a new position when something it depends upon has changed. It generally requires a fair amount of code to set up but is much cleaner to use once that's done. You'd make one assignment to something (such as a window size) and then all the new values propagate automatically, and only the values that need to propagate.

Most performant way to check onresize of dom object

This is a really specific need. Basically, I want to listen for when any of about 20 DOM objects change height or width. The change can be caused by window resize, by contained content changes, or by some javascript acting on the DOM object.
IE offers onresize() for any DOM object, not just the viewport, and this event is VERY performant. In fact, I can call a function onresize() and the function will fire continuously during the resize of the related DOM object. For example, I can adjust another DOM object to match the height of the object with onresize() and the two object's heights will mirror each other, with no perceptible lag, even when the resized object is animated to grow in height. AWESOME. But, for all browsers except IE, onresize() only works on viewport resize (browser/frame). Shucks.
Now I'm left with polling if the objects are resizing, which is very expensive and provides very noticeable lag when I'm trying to mirror the objects.
So, my question: what is the most performant way to raise an event when a DOM object is resized?
Sorry for the long question. Also, I am specifically NOT looking to achieve this with pure CSS. This is part of an experiment.
Thanks anyone who has time to reply!
Store the width and height in a variable that is later referenced in closure. The when the necessary event is fired check the clientHeight and clientWidth property against those stored in your variable.
Here is GUI I made about 6 months ago that solves that problem by example: http://prettydiff.com/jsgui/
The most performant way is to use an HTML/CSS layout that gets the browser to size the objects to match automatically. You would have to show us your exact HTML for us to advise how that might be done in your case.
As you have discovered, there are not good notifications for you to directly monitor the resizing of individual objects. About the best you can do is to monitor all possible events that might lead to a resize and then manually check each object yourself against a previously saved size or by comparing two objects to see if they match. That will likely not get you a lag-free experience, but it will be about as good as you can do with a javascript solution.

Performance of setting img src to unchanged value?

If I have an img tag like
<img src="example.png" />
and I set it via
myImg.src = "example.png";
to the same value again, will this be a no-op, or will browsers unnecessarily redraw the image? (I'm mainly interested in the behaviour of IE6-8, FF3.x, Safari 4-5 and Chrome.)
I need to change many (hundreds of) images at once, and manually comparing the src attribute might be a little bit superfluous - as I assume, that the browser already does this for me?
Don't assume the browser will do it for you. I am working on a project of similar scale which requires hundreds of (dynamic-loading) images, with speed as the top priority.
Caching the 'src' property of every element is highly recommended. It is expensive to read and set hundreds of DOM element properties, and even setting src to the same value may cause reflow or paint events.
[Edit] The majority of sluggishness in the interface was due to all my loops and processes. Once those were optimized, the UI was very snappy, even when continuously loading hundreds of images.
[Edit 2] In light of your additional information (the images are all small status icons), perhaps you should consider simply declaring a class for each status in your CSS. Also, you might want to look into using cloneNode and replaceNode for a very quick and efficient swap.
[Edit 3] Try absolutely-positioning your image elements. It will limit the amount of reflow that needs to happen, since absolutely-positioned elements are outside of the flow.
When you change a bunch of elements at once, you're usually blocking the UI thread anyway, so only one redraw after the JavaScript completes is happening, meaning the per-image redraw really isn't a factor.
I wouldn't double check anything here, let the browser take care of it, the new ones are smart enough to do this in an efficient way (and it's never really been that much of a problem anyway).
The case you'll see here is new images loading and re-flowing the page as they load, that's what's expensive here, existing images are very minor compared to this cost.
I recommend using CSS Sprite technique. More info at: http://www.alistapart.com/articles/
You can use an image that contains all the icons. Then instead of changing the src attribute, you update the background property.

Categories

Resources