Disabling HTML <option>s

If browsers worked as they should it would be pretty simple to disable some of the options in a select list:

<select>
    <option>Enabled</option>
    <option disabled>Disabled</option>
</select>

Of course IE doesn't support this part of the spec, and it's not included in the IE 7 beta, so it's unlikely it will be supported in the near future. So, one option that makes IE behave similarly to the disabled options is to replace them with <optgroup> tags. The <optgroup>s cannot be selected and with a little CSS they look like disabled options. However, the static solution suggested didn't work for me since I needed to be able to disable or enable options at runtime. So, with a little JavaScript (and the help of MochiKit's DOM functions) the options can be enabled or disabled dynamically:

var selectStyleDisabled = function(select) {
    forEach(select.options, function(opt) {
        if (opt.disabled) {
            var optgroup = OPTGROUP();
            updateNodeAttributes(optgroup, {
                label: opt.innerHTML,
                style: { color: 'graytext' }});
            optgroup._option = opt;
            swapDOM(opt, optgroup);
        }
    });
    forEach(select.getElementsByTagName("OPTGROUP"), function(optgroup) {
        var option = optgroup._option;
        if (option && !option.disabled) {
            swapDOM(optgroup, option);
        }
    });
};

addLoadEvent(function() {
    var selects = currentDocument().getElementsByTagName("SELECT");
    forEach(selects, function(select) {
        select._onfocus = select.onfocus;
        select.onfocus = function() {
            selectStyleDisabled(this);
            if (this._onfocus)
                return this._onfocus();
            return true;
        }
        selectStyleDisabled(select);
    });
});

The display of disabled options is updated in the "onfocus" method to make sure that changes in the disabled state of the options is reflected before the <select> is displayed again. Disabling options can be done directly to any of the option elements such as:

select.options[0].disabled = true;

However, a little care needs taken to re-enable options, since the <optgroup>s aren't in the "options" attribute of the <select>. The original option needs reenabled by accessing it through the "_option" attribute added to the <optgroup> element:

// reenable all disabled options in standards-compliant browsers
forEach(select.options, function(option) {
    option.disabled = false;
});

// reenable all disabled options in IE
forEach(select.getElementsByTagName("OPTGROUP"), function(optgroup) {
    optgroup._option.disabled = false;
});

To make sure that this IE hack only gets loaded in IE, put it in a .js file and wrap it in an IE conditional comment:

<!--if lt IE 8]>
<script type="text/javascript" src="select-ie-disabled-options.js"></script>
<![endif]-->