Dimos Dimos - 4 months ago 9
CSS Question

Why is browser's reflow not triggered, while Javascript is being executed?

In the UI of an application that I've been developing, I have been trying to show a "loader" div before a specific (synchronous) processing starts and hide it after the processing has ended. However, the loader does not appear at all during the whole processing.

I found out that browsers try to optimise the rendering process, by eliminating needless reflows, where possible. There are some ways to manually trigger the reflow process (for cases like mine), but I haven't managed to make any of them work. You can find below a minimal example, where the calculation of offsetHeight is used to trigger reflow, but the TEST div is not appearing at all.

<html>
<head>
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
</head>
<body>
<div id="test" style="display:none; background-color:red; height:500px">TEST</div>

<script>
function reflow() {
var t = $("#test")[0].offsetHeight;
$(window).trigger('resize');
window.getComputedStyle($("#test")[0], null);
$("#test").focus();
}

$("#test").show();
reflow();
console.log("Processing started");
var sum = 0;
for(i = 0; i < 100000000; i++) {
sum += i;
}
console.log("Processing ended");
$("#test").hide();
<script>
</body>
</html>


Working JSFiddle also here

Some of the ways attempted to force reflow are the following (as you can see in the script):


  • offsetHeight's calculation of element

  • window resize

  • window.getComputedStyle()

  • focus of element



NOTE: I know it can be solved, by adding a timeout. However, I want to find a way to force reflow, since timeout is a "hack" and is not appropriate for this functionality.

Answer

It finally seems that even when you trigger a re-flow event manually, as shown in my code, this event is added to an events queue. Since there is only 1 javascript thread per window, these events are handled only when the executing code has finished.

Even though I could not find an official documentation of this behaviour, I could neither trigger a synchronous reflow (while some code is executing) with any of the proposed techniques. So, the only solution seems to be to add a small timeout, so that thread is idle for a small period and can process the reflow event, before starting executing the remaining code (as suggested by @Dan Smolinske).

In order to avoid perplexing different functionalities, one can create a new method executeWithLoader, as below:

function executeWithLoader(callback) {
    $("#test").show();
    setTimeout(function() {
        callback();
        $("#test").hide();
    }, 50, this);
}

And call this function from the class the executes the processing in the following way:

function processing() {
    console.log("Processing started");
    var sum = 0;
    for(i = 0; i < 100000000; i++) {
        sum += i;
    }
    console.log("Processing ended");
}
executeWithLoader(processing);

I leave this as accepted answer for now, but if there is any future answer that describes a successful way to trigger re-flow, I will switch it.