views:

1920

answers:

4

I've recently run into a rather nasty bug, wherein the code was loading a <select> dynamically via JavaScript. This dynamically loaded <select> had a pre-selected value. In IE6, we already had code to fix the selected <option>, because sometimes the <select>'s selectedIndex value would be out of sync with the selected <option>'s index attribute, as below:

field.selectedIndex = element.index;

However, this code wasn't working. Even though the field's selectedIndex was being set correctly, the wrong index would end up being selected. However, if I stuck an alert() statement in at the right time, the correct option would be selected. Thinking this might be some sort of timing issue, I tried something random that I'd seen in code before:

var wrapFn = (function() {
    var myField = field;
    var myElement = element;

    return function() {
        myField.selectedIndex = myElement.index;
    }
})();
setTimeout(wrapFn, 0);

And this worked!

I've got a solution for my problem, but I'm uneasy that I don't know exactly why this fixes my problem. Does anyone have an official explanation? What browser issue am I avoiding by calling my function "later" using setTimeout()?

+1  A: 

Have you tried running the JavaScript code after the page is loaded completely (or at least when the DOM is loaded)?

EDIT: I wasn't really clear there, as Seb pointed out in the comments..
What I mean is, if the node that you are trying to edit with the JavaScript isn't loaded into the DOM tree when the JavaScript runs, it's values will not be set, since the node doesn't exist yet. But if you wait to run the JavaScript until after the DOM has loaded, it will correctly set the value of the node, since it now exists.

fredrik
I'd suggest not answering a question with another question, but explain why is it that your statement should mean a difference.
Seb
Your correct, added some clarification to my answer
fredrik
+5  A: 

setTimeout() buys you some time until the DOM elements are loaded, even if is set to 0.

Check this out: setTimeout

Jose Basilio
+1, Also worth noting that trying to understand every little 'hack' that's necessary to get things to work in IE6 is probably one of the most futile tasks in the world of web development :)
karim79
A: 

By calling setTimeout you give the page time to react to the whatever the user is doing. This is particularly helpful for functions run during page load.

Jeremy
+6  A: 

This works because you're doing co-operative multi-tasking.

A browser has to do a number of things pretty much all at once, and just one of those is execute JavaScript. But one of the things JavaScript is very often used for is to ask the browser to build a display element. This is often assumed to be done synchronously (particularly as JavaScript is not executed in parallel) but there is no guarantee this is the case and JavaScript does not have a well-defined mechanism for waiting.

The solution is to "pause" the JavaScript execution to let the rendering threads catch up. And this is what setTimeout() with a timeout of 0 does. It is like a thread/process yield in C. Although it seems to say "run this immediately" it actually gives the browser a chance to finish doing some none-JavaScript things that have been waiting to finish before attending to this new piece of JavaScript.

IE6 just happens to be more prone to this error, but I have seen it occur on older versions of Mozilla and in FireFox.

staticsan