10

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.

user2771609
  • 1,867
  • 1
  • 15
  • 36
  • i think there are some styles that will show up right away, if they affect layout. generally, dom stuff gets behind in a thread that does other stuff.a short timeout around the last two lines would make sure green shows first. – dandavis May 20 '15 at 19:19
  • 2
    Javascript is single threaded so your `longRunningFunction()` will block. Whether or not a redraw might happen while you function is running, I certainly wouldn't rely on it. – Matt Burland May 20 '15 at 19:19

2 Answers2

11

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.

Community
  • 1
  • 1
apsillers
  • 112,806
  • 17
  • 235
  • 239
  • Thanks. I'm trying to avoid intermediate states for showing up. Since it is mainly a UI nicety (I just don't want the user to see flickers), I feel fine relying on the reality of current implementations not rendering in parallel to event handlers. Nothing terrible will happen even if it changes... – user2771609 May 20 '15 at 20:01
0

Does this illustrate what you're aiming for?

function handleEvent(e){
    document.body.style.backgroundColor = 'green';
    alert();
    document.body.style.backgroundColor = 'red';
}
vizidev
  • 43
  • 4