Force Browser to Trigger Reflow While Changing CSS

Force browser to trigger reflow while changing CSS

Requesting the offsetHeight of an element does everything nicely. You can force a reflow using this function and passing it the element that styles have been changed on:

function reflow(elt){
console.log(elt.offsetHeight);
}

And call this where reflows are needed. See this example: http://jsfiddle.net/9WX5b/2/

EDIT: recently needed to do this, and wondered if there was a better way than to console.log it. You can't just write elt.offsetHeight as it's own statement, as the optimizer (Chrome's, at least) will not trigger a reflow because it is just accessing a property with no getter set, no need to even evaluate it. So, AFAIK the cheapest way to do this is void(elt.offsetHeight), as it does not know for sure if void has side effects or not. (could be overridden or something, idk).

Force Reflow in CSS transitions in Bootstrap

Bit of a late reply, but I'm tackling some issues with CSS transitions which I think relate to this bit of code you've found, and hopefully help you out with understanding it!

Basically, I'm toggling a class from Javascript / jQuery that adds css transitions to a dom element. The CSS of this element is then updated which causes the transition to occur. A simplified version of the code is below:

var myelement = $("myselector");

// Set z-indexes before the transition
myelement.css("z-index", 1);

var reflow = root.offset().left; // Re-flow the page

// Set the transition class on the element which will animate
myelement.addClass("trans");
myelement.css("width", 0 + "px"); // Animate to nothing

So if I uncomment my re-flow line, my transition will occur, but sometimes (it seems more often in safari) the z-index of myelement won't have been updated.

To me, it seems that in certain situations, the styles written to the dom are being buffered somewhere and not being flushed.

That's where the call to the left offset comes in. This is one of the properties that are said to cause a re-flow in the page. This is obviously usually a bad thing performance wise, but it seems necessary to prevent the css transitions picking up the wrong values.

There's an interesting Mozilla bug lodged which discusses the same subject. Might be of some interest. They suggest the addition of an API to properly start transitions from code.

This is also an interesting SO post about forcing re-flows.

Hope this helps! :)

How can I force WebKit to redraw/repaint to propagate style changes?

I found some complicated suggestions and many simple ones that didn’t work, but a comment to one of them by Vasil Dinkov provided a simple solution to force a redraw/repaint that works just fine:

sel.style.display='none';
sel.offsetHeight; // no need to store this anywhere, the reference is enough
sel.style.display='';

I’ll let someone else comment if it works for styles other than “block”.

Thanks, Vasil!

Why don't I see layout/reflow being triggered when changing and retrieving element positions?

Checking the Element.style.property value will not trigger a reflow no. This value is not the "computed value", and this doesn't require the layout to be recalculated.

const target = document.querySelector(".target");

target.style.left = "0px";

// logs "0px"
// even though the real computed value is "25px"
// from the !important rule in CSS
console.log( target.style.left );

target.classList.remove("begin");
target.style.left = "250px";
// now the computed style is correctly 250px
// but since we didn't trigger a reflow
// the transition doesn't kick in
.target {
transition: left 2s;
width: 50px;
height: 50px;
background: salmon;
position: absolute;
}
.begin {
/* will take precedence over the .style rule */
left: 25px !important;
}
<div class="target begin"></div>

Does `window.matchMedia` trigger a reflow?

I believe that the list is correct and matchMedia does not trigger a reflow. All media queries have been carefully designed to not depend on page content. For example, width has been specced as viewport width including the scrollbar so that the result doesn't change regardless of whether there is a scrollbar or not.

When does reflow happen in a DOM environment?

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.

Can I use javascript to force the browser to flush any pending layout changes?

We encountered a crazy problem with IE8 (Firefox, Chrome are fine). We use toggleClass('enoMyAddressesHide') on child element.

.enoMyAddressesHide{display:none}

But the parent(s) div container does not refresh/re-layout its height.

setTimeout(), read position, read width and height of element do not help.
Finally we can find out a working solution:

jQuery(document).ready(function ($) {
var RefreshIE8Layout = function () {
$('.enoAddressBook:first').css('height', 'auto');
var height = $('.enoAddressBook:first').height();
$('.enoAddressBook:first').css('height', height);
};

$(".enoRowAddressInfo .enoRowAddressInfoArea ul li img.enoMyAddresses").click(function () {
$(this).parent().find(".enoAllInfoInAddressBox,img.enoMyAddresses").toggleClass('enoMyAddressesHide');

RefreshIE8Layout(); // fix IE8 bug, not refresh the DOM dimension after using jQuery to manipulate DOM
});
});

It looks stupid, but it works!



Related Topics



Leave a reply



Submit