7

I have this code

var opt = $("select option:first");
opt.remove();

$("button").on("click", function() {
    $("select").prepend(opt).val(1);
});

That works fine in some browser. But, of course, IE isn't one of them. In IE the combo ends with the two options, but the text is in blank (there is no selected option). I assume this is because the option is still not loaded into the DOM. I assume that because I can easily fix this problem using this code instead:

var opt = $("select option:first");
opt.remove();

$("button").on("click", function() {
    $("select").prepend(opt);
    setTimeout(function() {
        $("select").val(1);
    }, 1);
});

However, I would prefer something nicer. Any ideas?

Note: I'm not looking for performance in the selector or things like that. The posted code is just a reduced example, not my real script.

Diego
  • 16,436
  • 26
  • 84
  • 136

3 Answers3

3

This seems to work on all browsers, though I have no idea why your original code failed in IE.

var opt = $("select option:first");
opt.detach();

$("button").on("click", function() {
    $("select").prepend(opt).prop("selectedIndex", 0);
});

http://jsfiddle.net/tEUxg/5/

James Montagne
  • 77,516
  • 14
  • 110
  • 130
2

IE using queue system for DOM manipulation. Use dequeue(): DEMO

var opt = $("select option:first");
opt.remove();

$("button").on("click", function() {
    $("select").prepend(opt).dequeue().val(1)
});
Ian
  • 50,146
  • 13
  • 101
  • 111
A. Wolff
  • 74,033
  • 9
  • 94
  • 155
  • 1
    That is truly bizarre. Do you happen to have any info on why this happens? Maybe a jquery blog post or something? This is the first I'm hearing of this. I'm wondering if maybe this isn't exactly what is happening but rather something in `dequeue` is interrupting the process long enough to let the DOM update. – James Montagne Dec 11 '12 at 14:57
  • I think you could find it in MSHTML (trident) specification. Unfortunately, i dont have any relevant link to post. – A. Wolff Dec 11 '12 at 15:12
  • As well as I can't understand it, I think its nicer @James way. However is really good to keep this in mind. +1! – Diego Dec 11 '12 at 15:16
  • 1
    @James Montagne: Turns out that it comes down to a simple accessor that's necessary on the select. If you add a dummy `.each(function() { this[0]; })` before the `.val(1)`, then it works. – pimvdb Dec 11 '12 at 15:21
  • I filed a bug since this is something jQuery could work around: http://bugs.jquery.com/ticket/13032. – pimvdb Dec 11 '12 at 15:31
  • @pimvdb Are you sure it's just IE9? And what do you mean it comes down to a simple accessor? You might want to include the other things that have "worked" to "fix" this problem that we've come across in this SO question...and even link to this SO question. – Ian Dec 11 '12 at 15:47
  • @Ian: In IE9 with IE7/8 emulated I don't see the bug. With "accessor" I mean accessing `this[0]`. (I did link to this question.) – pimvdb Dec 11 '12 at 15:49
  • @pimvdb Sorry, don't know how I missed the link to this question. And good, wasn't sure if anyone tried the emulated IE7/8. And I knew what you meant by "accessor", but that doesn't necessarily fix it...it's just one of the "solutions". As we've found out, using `delay`, `dequeue`, or (any) type of animation seems to work, where I'm sure there are more possibilities. As the OP pointed out, all you have to do is use `setTimeout` with the smallest timeout and that works too...it almost seems to be a timing issue. And it's interesting that `append` works fine... – Ian Dec 11 '12 at 15:54
  • @Ian: I found that the accessor fix works because I hunted down the code path of `.dequeue`, where somewhere such an accessor is used. Both `.dequeue` and `.delay` do effectively the same thing here: they call `$.data`, which is where the "fix" occurs (through an accessor). Not sure why `setTimeout` works, but then again, it often fixes things. – pimvdb Dec 11 '12 at 16:01
  • @pimvdb Huh, interesting. Yeah, not sure about `setTimeout` then after that find of yours... – Ian Dec 11 '12 at 16:13
1

It's silly, but another solution based off the original solution you were thinking to use is:

http://jsfiddle.net/N2UF4/1/

var opt = $("select option:first");
opt.remove();

$("button").on("click", function() {
    $("select").prepend(opt).delay(1).val(1);
});

Still not sure why this is necessary (or the dequeue from the other answer) but it seems to "fix" the problem as well.

Ian
  • 50,146
  • 13
  • 101
  • 111
  • This is all very strange. It seems as though calling anything animation related is forcing a DOM update. It seems you can put pretty much anything animation related in place of delay there and it works (fadeOut, etc.) – James Montagne Dec 11 '12 at 15:16
  • As well as I can't understand it, I think its nicer @James way. However, your way is nice and works. +1! – Diego Dec 11 '12 at 15:18
  • @Diego I agree James' way is better - it's the real way this operation should be completed. – Ian Dec 11 '12 at 15:42