Naturally, a task as simple as adding and removing options to a select element in JavaScript turns out to be a 2 hour pain. Especially if you want to please a wide range of browsers. For my purposes, Prototype.js was the weapon, Insertion.After was the ammo, and adding an option at any position in the drop down was the target. The first attempt was straightforward:

new Insertion.After(node, '<option> </option>');

In this case, node is one of the options already in the select element, and the second parameter designates the markup to add after it. As expected, this worked great in Firefox and Safari. As I grudgingly opened Internet Explorer, I was bombarded with a wave of errors and an select element not functioning properly. It turns out the whole innerHTML on a drop down doesn’t work out so well.

A bit of detective work led me to a bug report confirming the innerHTML problem. In the spirit of removing any sort of browser detection, I decided to see how IE6 would like to handle things, and do it the hard way. Two options presented themselves:

el.add(optn, pos);

and

el.options.add(optn, pos);

I began with the second one, and was relieved to see it work in IE6. A click and a keystroke later, and Safari started breaking on me. Apparently the W3C recommends the add method be part of the actual select element, and not the options. So I tested the first approach, and it worked in IE6. Since it followed W3c recommendations, I figured I was set. But then Safari decided to always add the option to the end of the list, and Firefox yelled at me:

uncaught exception: [Exception… “Could not convert JavaScript argument” nsresult: “0x80570009 (NS_ERROR_XPC_BAD_CONVERT_JS)”

Finally, I gave in and took the browser detection approach.

if(Prototype.Browser.IE) {
    var optn = document.createElement("OPTION");
    optn.text = ' ';
    optn.value = ' ';
    el.options.add(optn, pos);
}
else new Insertion.After(node, '<option> </option>');
HTML Form Builder
Ryan Campbell

Adding Options to a Select Element by Ryan Campbell

This entry was posted 3 years ago and was filed under Notebooks.
Comments are currently closed.

· 8 Comments! ·

  1. Tobie Langel · 3 years ago

    Hi Ryan,

    That’s been fixed in SVN trunk. If you’re running edge you can now write:

    $(element).insert({bottom: 'foo'});

    or:

    $(element).insert('foo');

    (Element#insert defaults to bottom if no option is specified.)

    This syntax will replace new Insertion... in the next Prototype release (which will be kept for backwards compatibility).

  2. Morgan Roderick · 3 years ago

    Yeah, I’ve run into this one on more than one occasion. After a period of time, I tend to forget, and run into it again.

    Good to see that the fantastic libraries out there are handling more and more of this bllx for us.

  3. Ryan Campbell · 3 years ago

    Thanks for the heads up, Tobie. I’m with Morgan — it’s good to know there are people a lot smarter than I am staying on top of this stuff.

  4. Subbu · 3 years ago

    Hi Ryan, I am a bit confused with the usage of ‘el.options’. Is ‘el’ the resultant of something like $(‘someElement’)? Then what is options method? Thanks.

  5. Bramus! · 3 years ago

    Found this piece of code somewhere in the archive:

    try {
    el.add(option, null); // doesn't work in IE
    } catch(e) {
    el.add(option); // works in IE
    }
    }

    Quite odd, but it works (looks like the null pos param is messing things up in IE) although it can’t be used to insert elements anywhere but at the end. Maybe I should revise it and use your code ;-)

    @ Tobie: thanks for the info :-)

    @ Subbu: el is the element indeed retrieved by document.getElementById() (or the prototype $() shorthand). “.options” is a property of that element. (TIP: do a $(‘yourList’); in the FireBug console and click on the result to browse its properties and methods ;-))

  6. Subbu · 3 years ago

    @Bramus!: Thanks a lot :)

  7. Tom Stewart · 3 years ago

    Why not simply use Prototype’s Ajax.Updater() and [re]load a complete SELECT options list all at once into a DIV tag set?