2005/10/23

Another stupid IE bug: selectedIndex on IE6

Presupposing these functions:

function addNewOption(select, value, label) {
var option = document.createElement("option");
option.setAttribute("value", value);
option.appendChild(document.createTextNode(label));
select.appendChild(option);
}

function getOptionIdx(select, value) {
if(!select.options) return -1;
for(var oNum = 0; oNum != select.options.length; oNum++) {
if(select.options[oNum].value == value) return oNum;
}
return -1;
}
Now watch carefully:
var optVal = "1";
var select = $("exampleSelect"); //prototype shortcut

addNewOption(select, optVal, "one");

This code retrieves a select by ID and adds an option to it. (The select is a single-value select.) The following code selects that option:

select.selectedIndex = getOptionIdx(select, optVal);


Or it would do, if it worked in IE. Apparently selectedIndex is readonly in IE4, which I don't care about. However, selectedIndex is also readonly in IE6, under certain circumstances, but it just fails silently. The workaround for the IE4 bug was as follows:

select.options[getOptionIdx(select, optVal)].selected = true;

This fails in IE6 with the stunningly unhelpful message "Could not set the selected property. Unspecified error."

HOWEVER, this code does work:

var optIdx = getOptionIdx(select, optVal);

alert("optIdx="+optIdx);

select.options[optIdx].selected = true; // [1]


Removing the alert (or logging statement or whatever) gets you straight back to square one. Wierder still, surrounding [1] with a try-catch block will throw an "undefined" error, and set the select to the value, which I guess is the workaround I need.

Needless to say, I don't have this problem on FireFox. My completely uneducated guess is that DOM tree operations are slightly asynchronous, and that the time it took for the JS interpreter to move onto the next instruction was longer than the time it took for the DOM update thread to finish making changes, when computers were slower than they are now. I have no idea.

17 comments:

  1. Anonymous4:30 am

    This workaround may be for some who found this post by searching for a solution to this problem (I didn't find one so I had to come up with this myself).

    The problem was appearing for me in an onLoad call, might be the same with a call from embedded script I suspect. I noted also that an alert() with the code would make it work so I figured that maybe it's a problem with the page loading procedure and interrputing that helps.. So why not break out of the onLoad?

    My solution is to put a setTimeout() call to my option setting function in the function called by onLoad. So it now looks like: setTimeout("initSelections()", 100); with function initSelections() {} doing the select.selectedIndex = X; and working properly.

    100ms is fine for me, I suspect it could go lower and still be fine.

    ReplyDelete
  2. Anonymous12:26 pm

    IE is awful.
    I encountered this problem during work, and since I had to fix multiple select boxes the setTimeout solution wasn't working for me, as the pointer for the option already changed by the time it executed the script.

    Anyways, here is a fix that actually worked for IE. instead of using
    ele.selected = true;
    I used
    ele.setAttribute('selected',true);

    And it actually worked.

    Conclusion: If I spent as much time writing real code at work instead of fixing IE-specific bugs, you would have all known me by now.

    ReplyDelete
  3. Anonymous11:18 pm

    I'm struggling with this bug right now (I'm trying to set a [select] to a default value on [body onload]). I've tried all of the suggested solutions, but none of them work.

    Annoyingly, IE refuses to recognize the options property of my select element - it keeps saying that it's null or not an object. This remains true even if I use the window.setTimeout() method to delay execution by 500ms.

    I really, really hate IE6. Thanks for the post, though - it's at least given me somewhere to start.

    ReplyDelete
  4. Anonymous3:38 pm

    Do the following:

    while(true) try { option.selected = true; break; } catch(e) {}

    It solves this stupid MSIE bug (a kind of synchronization/locked variable error)

    Thanks for your insights

    Jose M. Arranz

    ReplyDelete
  5. Anonymous7:39 pm

    A finer solution:

    if (option.selected != myValue)
    while(true) try { option.selected = myValue; break; } catch(e) {}

    Otherwise could enter in an infinite loop.

    Jose M. Arranz
    JNIEasy: C/C++ meets Java

    ReplyDelete
  6. Anonymous10:18 am

    This is really a GREAT help, thanks a lot .!

    ReplyDelete
  7. Anonymous7:11 am

    This was of great help. Using setAttribute works like a charm! Thanks a lot

    ReplyDelete
  8. Anonymous5:16 pm

    Thanks a lot, a whole wasted afternoon was ended by this article

    ReplyDelete
  9. Anonymous11:55 am

    Thanks a lot, it solved my problem.

    ReplyDelete
  10. Great bit of info for selecting multiple options in internet explorer

    ReplyDelete
  11. great help... I found this useful
    thanks

    ReplyDelete
  12. Anonymous4:52 am

    Great help.. Thanks

    ReplyDelete
  13. Thanks. This save my life.

    ReplyDelete
  14. Anonymous8:47 pm

    the option tag requires a value attribute in IE. Simply set the value to the same value that is between the option tags and you're set.

    ie (no pun intended)
    select
    option value="Blue">Blue option
    option value="Red">Red option
    select

    (Sorry, blogger doesn't allow full tags)

    Then IE can access it with JavaScript by element.value.

    ReplyDelete
  15. Anonymous1:31 am

    Thanks for posting this. Keep up the good work!

    ReplyDelete
  16. thanks this worked for me-
    try { dropdown.selectedIndex = i; } catch (e) { };

    My select code was in one of the functions called onLoad as well but I didnt need to try the setTimeOut option though that has worked for me before when I am manipulating the DOM from a function within onLoad on IE

    ReplyDelete
  17. ok it didnt work as i specified above. that only works when i do Ctrl+F5

    had to use the recursive stuff-

    if (dropdown.options[i].selected != true) while (true) try { dropdown.options[i].selected = true; break; } catch (e) { }

    I feel sad :(

    ReplyDelete