A browser UI trick using setTimeout
Posted on August 28th, 2007 by TedRecently, when working on a DHTML slideshow based on Prototype and script.aculo.us, I ran into a problem with JavaScript and updates to the page UI. I intended the portfolio code to do the following:
- hide the current image
- replace the current image with a new image
- fade in the new image
However, in my first implementation, I saw the following behavior:
- the current image would disappear
- the new image would flash immediately in its place
- the new image would disappaer
- the new image would fade in
Effectively, the portfolio code performs the following steps (simplified for example):
Element.hide( id );$( id ).src = source of new image$( id ).onload = function() { new Effect.Appear( id ) }
The problem was that in some browsers I tested (Firefox 2.0.0.6 on Windows and Mac, IE 6/7 on Windows), line 1 of the portfolio code would not appear to execute until after line 2. It turns out that for these two sets of browsers, UI rendering occurs in the same thread as JavaScript execution. The browser “queues” DOM manipulations until after the JavaScript yields. In my example, the Element.hide does not take effect until after the src attribute of the image has been changed. So, the new image suddenly replaces the old image, the new image is hidden, and then it slowly fades in.
The trick I used to solve this was to use setTimeout to effectively “yield” control so that the browser could update the page UI (again code simplified for example):
Element.hide( id );setTimeout( function() { $( id ).src = source of new image }, 0 );$( id ).onload = function() { new Effect.Appear( id ) }
Here’s a complete, yet simple example. Refresh your browser to reset the state of the page. You can also play with the example here.
ready
If you click on the first link, nothing will appear to happen for 3 seconds or so, then the message will change to “go!” However, if you click on the second link, the message will immediate change from “ready” to “set…” and after 3 seconds, to “go!”
Be careful with this setTimeout trick. It is easy to abuse it. I have implemented a queue where setTimeout is used to yield control to the browser UI between commands previously enqueued commands. The code became unmanageably complex. Yielding control in this fashion does not make JavaScript threaded, but does introduce asynchronous behavior which may cause your code to execute in an unexpected order.






