5

Before I dive into the question. Let me state that by Event Loop I am referring to http://en.wikipedia.org/wiki/Event_loop. This is something that browsers implement. For more information, read this: http://javascript.info/tutorial/further-javascript-features/events-and-timing-depth.

This question is hard and long, so, please try to bear with it! And I do appreciate all answers!


So. Now, as I understand it, in JavaScript there is a single main thread (in most browser environments, that is). So, code like:

for (var color = 0x000; color < 0xfff; color++) {
    $('div').css('background-color', color.toString(16));
}

will produce an animation from black to white, but you won't see that because the rendering is done after the code has been processed (when the next tick happens -- the browser enters the Event Loop).

If you want to see the animation, you could do:

for (var color = 0x000; color < 0xfff; color++) {
    setTimeout(function() {
        $('div').css('background-color', color.toString(16));
    }, 0);
}

The above example would produce a visible animation, because setTimeout pushes a new event to the browser Event Loop stack which will be processed after there is nothing running (it enters the Event Loop to see what to do next).

It seems that the browser in this case have 0xfff (4095) events pushed into the stack, where each of them are processed with a render process in between them. So, my first question (#1) is that when exactly does the rendering take place? Does it always take place in between the processing of two events in the Event Loop stack?


The second question is about the code in the javascript.info website link I gave you.

...
  function func() { 
    timer = setTimeout(func, 0)
    div.style.backgroundColor = '#'+i.toString(16)
    if (i++ == 0xFFFFFF) stop()
  }

timer = setTimeout(func, 0)
....

My question here is that will the browser push a new "rendering" event to the Event Loop stack every time it reaches the div.style. ... = ... line? But does it not first push an event due to the setTimeout-call? So, does the browser end up in a stack like:

setTimeout event
render event

Since the setTimeout call was processed before the div style change? If that's how the stack looks like, then I would assume the next time the browser enters the Event Loop it will process the setTimeout's callback and end up having:

rendering event
setTimeout event
rendering event

and continue with the rendering event that the earlier setTimeout call produced?

Tower
  • 98,741
  • 129
  • 357
  • 507

2 Answers2

6

Q1: Not necessarily. Browsers to varying degrees implement optimizations. For example, they may wait to collect several style changes before triggering an expensive recalculation of the layout. So the answer is: depends on the specific browser.

Try this: http://taligarsiel.com/Projects/howbrowserswork1.htm#Render_tree_construction (the document is dated Oct 2009 - i.e. it is sufficiently up to date)

Q2: The rendering is not necessarily the same as the JS execution - that's two different engines. Ths JS engine is not responsible for the rendering, it just interfaces with the render engine. It seems to me the main message for your second question is this independence of the JS from the rendering engine. Remember, a browser (or a webpage) does not need Javascript, their main purpose is to render HTML based on CSS style rules. Javascript is just one way to manipulate the HTML (the DOM tree really) and the style rules.

Note that you can force rendering by reading a style definition - at this point the rendering engine has no choice but process any outstanding style changes, especially if it involves any position changes. That's why one should remove objects from the rendering tree (e.g. by setting display:none - visibility:hidden is NOT enough since the element's size is still considered for layout) before doing a lot of style changes or adding a lot of elements, e.g. when lots of rows are added one by one (a "for" loop) to a table.

Not part of the question at all - but since I just mentioned a difference between display:none and visibility:hidden, that's also a consideration when adding hidden position:absolute elements like dialogs. While there is no visible difference whether an absolutely positioned element is hidden from you using one or the other method, internally there IS a big difference: when hidden using visibility:hidden the element is part of the rendering tree, with display:none it is not. So, if one has such an element that needs to be toggled a lot one should use visibility:hidden, because when the "display" style is switched between "none" and e.g. "block" the browser has to render it first.

greg-449
  • 109,219
  • 232
  • 102
  • 145
Mörre
  • 5,699
  • 6
  • 38
  • 63
1

The article you mention only considers Javascript. A lot more happens in the browser; reflowing and repainting are/can be triggered by a lot more things; take a look at the following links for more info on this.

I wouldn't use setTimeout for this purpose.

Edit: As per the comments, the recommended way is to use requestAnimationFrame. As of this writing, this is only available in unstable releases of most browsers. There are however several libraries available providing cross-browser access to it, and fall back to using setTimeout if necessary.

Take a look at this demo for an example working in old browsers, as well as in new ones: http://paulirish.com/2011/requestanimationframe-for-smart-animating/

Martijn
  • 13,225
  • 3
  • 48
  • 58
  • Responding to the last sentence: To achieve animation (i.e. changing one or more CSS style attributes over time) he should indeed use setTimeout, otherwise: 1) execution blocks the JS engine (and the browser!), 2) no visible change (jumps to the end of the animation with no steps in between) because the optimized rendering engine detects lots of style changes and collects most or all of them before triggering a re-rendering. – Mörre Mar 05 '11 at 12:37
  • You're right, sorry. I misread the question (and got confused with another question I was reading at the time). – Martijn Mar 05 '11 at 12:46
  • Actually, one should use `window.requestAnimationFrame()` if that is available. – Tower Mar 05 '11 at 14:27
  • requestAnimationFrame: Good to know for TOMORROW, but nothing to care about TODAY. that's for the (WebGL) future, no currently released browser has it. Besides, so far implementations differ between browsers. – Mörre Mar 05 '11 at 15:14
  • 1
    It's supported on Chrome currently. Firefox 4, which is soon out, supports it. I would say in three months, 1/3 of the browser market supports it. – Tower Mar 05 '11 at 17:04
  • @Mörre: For cross-browser `requestAnimationFrame`, see here: https://gist.github.com/838785. If the browser supports it, it’ll return the native one; otherwise, it’ll make do with `setTimeout`. Yesterday-proof _and_ tomorrow-proof! :-) – Martijn Mar 05 '11 at 18:13
  • @rFactor: Just because a new version is released doesn't mean a week later everyone is going to be using it. Current Chrome does not support it - unless you mean current DEVELOPMENT version. Yes, a point was made (pointing to upcoming feature) - and I added another one (that it IS "upcoming"). Okay??? – Mörre Mar 05 '11 at 18:30
  • @Mörre: I am not using a Chrome development build. I never said one week, I said three months. In three months every one is using the current Chrome beta build out of Chrome users (or newer), and most of the Firefox base is on version 4. Even though not 100% of the market does not support the functionality, you should have a poly-fill for those that do. – Tower Mar 06 '11 at 12:25
  • No. THERE IS NO requestAnimationFrame in Chrome 9! Neither window.requestAnimationFrame nor window.webkitRequestAnimationFrame is defined as one can easily check. A look at the relevant discussion e.g. here http://code.google.com/p/chromium/issues/detail?id=64848#makechanges also shows this is recent. This WRONG information is the only reason I post yet again - it is sooooo useless, we all made our points and this has nothing to do with the original question. Again: the existence of this new API was pointed out (good!). I pointed out it's new - so ALL the info has been given. – Mörre Mar 07 '11 at 10:16
  • @Mörre: relax, dude! You’ve made your point, no use hammering it (or us) to death. It’ll be moot in a couple months anyway. – Martijn Mar 07 '11 at 10:27
  • @Mörre: Chrome 9? Chrome 10 is stable... and Firefox 4 RC is already out. I still think in 3 months we have 1/3 of the web capable of doing this. I might be wrong, it could be just 20%. Still worth using by far, especially because iPad 1, 2 and mobile is so slow. Since they are webkit based mostly, it is something of concern today. – Tower Mar 09 '11 at 19:21
  • Unfortunately, iPad 1 doesn't seem to support it, but let's have our hopes high that most of the products on the mobile market do, and if not, as soon as possible. – Tower Mar 09 '11 at 19:39
  • @rFactor: WTF? Released two days ago! Did you look at the DATE??? Do you have anything NEW to add? – Mörre Mar 09 '11 at 22:12
  • 2
    @Mörre: I just checked the statistics on gs.statcounter.com and it looks like that 24.82% of the web already support it! And we are still more than one month away from my estimated "three months" figure :)! Definitely something to worry today! – Tower Apr 23 '11 at 16:11