Jump to 0 top | 1 navigation | 2 content | 3 extra information (sidebar) | 4 footer | 5 toolbar


Content

Workarounds for IE: Disabled Options

How it all started...

Yes, I admit, I am one of those post-modern programmers who search the Web for solutions and code. Time to refund something.

One task I often come across at work is to find workarounds for Internet Explorer HTML, CSS, or JavaScript - um - deficiencies. As you can imagine, this task can consume lots of time and would often not be possible without post-modern programming techniques.

Today's topic is IE's lack of supporting the disabled attribute for option elements. It simply ignores the attribute: a disabled option is still selectable. This is not even fixed in IE7.

Several solutions can be found on the Web, which fall mainly into two categories:

  • Complicated JavaScript solutions that try to change the selection afterwards when detecting that the selected item is actually disabled (e.g. by Alistair Lattimore and by Kaleb Walton).
  • The idea to simply replace a disabled option by an optgroup, as described by Ben.

I like the second approach much better, not because it refrains from using JavaScript, but because the behavior in the browser is as intended and it does not interfere with other event handlers. But what I do not like about the second solution is that one has to care about the IE bug during rendering the HTML code.

For the first solution, some posts suggested to use DHTML Behaviors. Although this is an IE-prorietary feature, it is really okay to use it here, namely as a workaround for an IE-specific problem.

So my idea was to combine the HTML of the second approach with the "JavaScript will take care of it" attitude of the first approach and came up with the following solution which did not seem to be available on the Web before. (In fact, this idea became the missing push to eventually get me blogging.)

Fables of the Reconstruction

To cite from Ben's solution:

The key is to replace your disabled options with an optgroup. Why does this work?

  • optgroups are already disabled — you can’t select them, so you get immediate feedback.
  • optgroups don’t look disabled, but you can use CSS to change the look.

As I said, the only real drawback of Ben's solution is, as he mentions, that you have to care about constructing the IE-specific HTML yourself. So why not have JavaScript do the reconstruction inside the browser? All the JavaScript has to do is scan all option elements for the disabled attribute and replace them by optgroups. Matters become a bit more complicated when disabled options inside optgroups are also supposed to work, but we'll also cover that use case.

For the following code fragment, let's assume there is a variable element refering to an option element. We have to check its disabled property and, to make sure we produce no nested optgroups, the parent node not to be an optgroup. Then, we retrieve the label of the option element, which is its innerHTML, and replace it by a newly constructed optgroup with that label and the style suggested by Ben:

  if (element.disabled && element.parentNode.nodeName!="OPTGROUP") {
var label = element.innerHTML;
var optgroup = document.createElement("<optgroup label='"
+ label
+ "' style='color:graytext; font-style:normal;'/>");
element.parentNode.replaceChild(optgroup,element);
}

I could have used a simple external JavaScript file that adds an onload event handler and scans the whole document for option elements. Instead, I chose to use DHTML Behaviors, which leads to an elegant and concise solution and is "allowed" in this case for the reasons explained above.

For problems like this one, where you just want to execute some JavaScript code on all elements that match some CSS rule, DHTML Behaviors are really easy to handle. Just define an IE-specific CSS rule like this:

option { behavior: url("disabled-option.htc"); }

Because this rule is IE-specific and standard-compliant browsers may complain about it (at least Firefox issues a warning in its console), it should go into an IE conditional comment:

<!--[if IE]>
<style>
option { behavior: url("disabled-option.htc"); }
</style>
<![endif]-->

An HTC file is essentially a JavaScript source code file wrapped with some meta information. In this case, the meta information is trivial, and the current element is handed in as a parameter named element (what coincidence and surprise!), so that we just have to wrap the JavaScript code fragment given above with

<public:component>
<script>
...
</script>
</public:component>

and put it into a file called "disabled-option.htc".

Group Pressure

Ben points out that since you cannot nest optgroup elements, it requires a bit more tricks to find a workaround for disabled options inside an option group. He suggests the following:

  • Use non-breaking spaces to indent the menu items as a way to emulate a nested optgroup.
  • Use other empty optgroup items (with no extra CSS formatting) to serve as the group headings.

That means we need another Behavior for optgroups that checks whether the group contains a disabled option, and if so, moves all options to the top level, adding non-breakable spaces to emulate indention, and converts disabled options to optgroups on the fly. Let's call this Behavior "optgroup-with-disabled-options.htc". If you need this feature, add a corresponding CSS rule to the style that is only visible for IE (see above):

    optgroup { behavior: url("optgroup-with-disabled-options.htc"); }

The contents of "optgroup-with-disabled-options.htc" looks like this:

<public:component>
<script>
// check whether it contains a disabled option:
for (var i=0; i<element.childNodes.length; ++i) {
if (element.childNodes[i].nodeName=="OPTION" &&
element.childNodes[i].disabled) {
// move all options one level up, adding non-breakable
// spaces to indent and converting disabled options:
var parent = element.parentNode;
var insertionPoint = element.nextSibling;
for (var i=element.childNodes.length-1; i>=0; --i) {
var option = element.childNodes[i];
if (option.nodeName=="OPTION") {
var label = option.innerHTML;
if (option.disabled) {
element.removeChild(option);
option = document.createElement("<optgroup label=' "
+label
+"' style='color:graytext; font-style:normal;'/>");
} else {
option.firstChild.data = " "+label;
}
insertionPoint = parent.insertBefore(option,insertionPoint);
}
}
break;
}
}
</script>
</public:component>

Note that to emulate IE's standard indention depth, you need six spaces for normal options, but only four spaces for option groups that simulate disabled options (probably because the option group is bold?).

Conclusion

Putting it all together, you can now use the disabled attribute on option elements, and the following happens:

  • All non-IE browsers take the conditional comment as a comment and do nothing special. Disabled options work, anyway.
  • IE uses the style inside the conditional comment and thus defines the given Behaviors for all option and optgroup elements.
    • For each option element, the JavaScript code defined in "disabled-option.htc" is executed and replaces disabled options by optgroups.
    • For each optgroup element, the JavaScript code defined in "optgroup-with-disabled-options.htc" is executed, moves all options inside an optgroup that contains any disabled option to top level, indents them, and again replaces disabled options by optgroups.

The result looks like this:

Disabled Option in IE

 

The advantages of this new solution:

  • You only have to include a few lines in the head of an HTML page which are treated as comment by any validator and any standard-compliant browser.
  • Then, you can use the standard-compliant way for disabling options and it will work even in IE.
  • All usual event handlers defined on the select element work as before.
  • For the anti-JavaScript-lurkers: If JavaScript is turned off, the situation isn't any worse than before: simply, the IE bug is still present.

The only disadvantages I could think of:

  • The DOM is manipulated at run-time. If for example another script is manipulating the options (quite a common use case), it may have to be adapted to be aware of the disabled-option Behavior.
  • Maybe a small rendering performance impact if you have many many options on a page.
  • IE ignores most CSS style on options. Luckily, the font color can be changed to graytext, but font-style and font-weight do not seem to have any effect; the text is always bold and italic. I think this minor design deviation is bearable.

Please let me know if you found this solution useful!

  • No ratings
  • No ratings
  • No ratings
  • No ratings
  • No ratings
  • 0 ratings

2 trackbacks

1. Somms.NET &raquo; Blog.., Aug 20, 2007 7:19:52 PM

2. New in rails 2.3 - disab.., Mar 9, 2009 2:29:59 AM

3 comments

1. Chad (anonymous), Jun 30, 2008 8:12:17 PM #

Excellent post. Saved me a lot of time. Nice way to take an idea and make it that much better.

2. Schnies (anonymous), Jul 30, 2008 10:49:40 AM #

Many Thanks, work for me excellent.
Short Info: You have to declare the Variable 'element' not in the htc-File, but after the HTML-Form. For Example:



Heino
Michael Jackson
Tom Waits
Nina Hagen
Marianne Rosenberg
Hansi Rosenberg



var theform;
if (window.navigator.appName.toLowerCase().indexOf("microsoft") > -1) {
theform = document.Testformular;
}
else {
theform = document.forms["Testformular"];
}
var element = Testformular.Auswahl;


Best Wishes,
Schnies

3. Honti, Balázs (anonymous), Dec 8, 2008 4:34:12 PM #

Hello! Recursive way of disabling whole optgroups:
Modify optgroup-with-disabled-options.htc file as:

down=element.disabled;
...
if (element.childNodes[i].nodeName=="OPTION" && (down||element.childNodes[i].disabled))
...
if (down||option.disabled)...

Good luck,
Balázs

Leave a comment


Already have a login?