Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

If I make DOM changes in a Javascript event handler, are the changes rendered incrementally, or just once at the end?

Say I have the following javascript event handler:

function handleEvent(e){
    document.body.style.backgroundColor = 'green';
    longRunningFunction();
    document.body.style.backgroundColor = 'red';
}

Will the browser first display a green background and then switch it to red? Or display the red background directly?

According to my testing it displays red directly at the end of the event handler. But is that part of the specification, or just incidental to how browsers happen to be implemented?

UPDATE:

I should clarify that I am not "aiming" for this effect. Rather, I want to have some guarantee that it does not happen. Some of my event handlers change many things, and it makes my life easier if I can assume that none of the intermediate states are rendered.

like image 1000
user2771609 Avatar asked May 20 '15 19:05

user2771609


1 Answers

Here is what will happen in the JavaScript execution environment:

  1. document.body.style.backgroundColor will change its value to 'green'.
  2. A long-running process will run.
  3. document.body.style.backgroundColor will change its value to 'red'.

It is unspecified how the changes in the backgroundColor property will affect the appearance of the page, if the JavaScript thread is blocked.

Here's an example. If you click the word hello, you will see a slight delay, and then the background will turn red:

function handleEvent(e){
    document.body.style.backgroundColor = 'green';
    longRunningFunction();
    document.body.style.backgroundColor = 'red';
}

function longRunningFunction() {
    for(var i=0; i<2000000000; ++i) { +i; }
}

document.body.addEventListener("click", handleEvent);
<div>hello</div>

This is because the browser's rendering engine that redraws in response to changes to CSS properties is blocked by the long-running blocking JavaScript function. This is not specified behavior, but rather an implementation reality for all browsers. See How can I force the browser to redraw while my script is doing some heavy processing? for a similar question.

I think any given browser could choose to run a separate redraw thread concurrent with the JavaScript thread, but I don't believe any major browsers currently do so. There may be race-condition complexities inherent in such an approach; I've never written a browser so I don't know.


If you want to show intermediate state, you can use a very quick setTiemout call after setting backgroundColor to green, to clear the JavaScript function stack and allow the renderer to redraw the page:

function handleEvent(e){
    document.body.style.backgroundColor = 'green';
    setTimeout(function() {
        longRunningFunction();
        document.body.style.backgroundColor = 'red';
    }, 4);
}

This queues up the longRunningFunction and second color change to run in 4 milliseconds in the future. During that idle 4 milliseconds, the renderer has chance to redraw the page.

like image 54
apsillers Avatar answered Nov 16 '22 01:11

apsillers