2006/07/21

Emulating Disabled Options in IE with DHTML Behaviors

Summary

Internet Explorer 6 does not implement disabled OPTION's in a SELECT. "Select, Option, Disabled And The JavaScript Solution" talks about a solution to emulate them using JavaScript. In this article, I will show a cleaner solution using DHTML Behaviors.

Advantages

Advantages of my DHTML Behaviors solution over other JavaScript solutions are:
  • No mess with JavaScript
    You don't have to add JavaScript to every SELECT tag which may contain disabled options.
    If you already have some complicated JavaScript, just leave them as they are.
  • No mess with DOM operations
    If you already have some complicated DOM operations, just leave them as they are.
  • Only a small change in CSS
    To implement the solution, you only need to make a small change to existing CSS.
  • Works completely transparent
    The solution works as if disabled options are supported from the beginning.
    You can use this solution with JSF, with which it is difficult to change output HTML.

The Solution

To demonstrate the solution, look at the three files below:
  • sample.html
  • sample.css
  • sample.htc
The first two files are just to test the emulation. The last HTC file is the core of the solution. (Download is available at the bottom of this page.)
sample.html
<html>
<head>
<link rel="stylesheet" type="text/css" href="sample.css">
</head>
<body>
<form>
<select>
<option>Option 1</option>
<option>Option 2</option>
<option disabled>Option 3</option>
</select>
</form>
</body>
</html>
Above is a simple HTML file with disabled options. Normally, disabled options are displayed as non-disabled options in IE6. Note that external stylesheet sample.css is loaded. If you like, you can internalize it.
sample.css
select, option {
  behavior: url(sample.htc);
}
Above is a simple CSS file which will be loaded with the HTML file. DHTML Behaviors are attached to SELECT and OPTION. Actual behaviors are defined in sample.htc.
sample.htc
<?xml version="1.0" encoding="ISO-8859-1"?>
<PUBLIC:COMPONENT LIGHTWEIGHT="true">
<PUBLIC:ATTACH EVENT="ondocumentready" ONEVENT="onDocumentReady()" />
<PUBLIC:ATTACH EVENT="ondetach" ONEVENT="onDetach()" />

<SCRIPT type="text/javascript">
//<![CDATA[

var nLastSelectedIndex;
var fOnChangeOriginal;

// event handlers

function onDocumentReady() {
  var sTag = element.tagName.toLowerCase();
  if (sTag == "select") {
    attachEvent("onchange", onChangeSelect);
    attachEvent("onpropertychange", onPropertyChangeSelect);
    nLastSelectedIndex = element.selectedIndex;
    hackOnChange();
  } else if (sTag == "option") {
    attachEvent("onpropertychange", onPropertyChangeOption);
    emulateOption();
  }
}

function onDetach() {
  var sTag = element.tagName.toLowerCase();
  if (sTag == "select") {
    detachEvent("onchange", onChangeSelect);
    detachEvent("onpropertychange", onPropertyChangeSelect);
  } else if (sTag == "option") {
    detachEvent("onpropertychange", onPropertyChangeOption);
  }
}

//

function onChangeSelect() {
  if (element.options[element.selectedIndex].disabled) {
    element.selectedIndex = nLastSelectedIndex;
  } else {
    nLastSelectedIndex = element.selectedIndex;
    if (fOnChangeOriginal != undefined) {
      fOnChangeOriginal();
    }
  }
}

function onPropertyChangeSelect() {
  var sChangedPropertyName = event.propertyName.toLowerCase();
  if (sChangedPropertyName == "onchange") {
    hackOnChange();
  } else if (sChangedPropertyName == "selectedindex") { // contributed by Zecc
    nLastSelectedIndex = element.selectedIndex;
  }
}

function onPropertyChangeOption() {
  var sChangedPropertyName = event.propertyName.toLowerCase();
  if (sChangedPropertyName == "disabled") {
    emulateOption();
  }
}

// hack onChange attribute of select tag

function hackOnChange() {
  detachEvent("onpropertychange", onPropertyChangeSelect);
  fOnChangeOriginal = element.onchange;
  element.onchange = null;
  attachEvent("onpropertychange", onPropertyChangeSelect);
}

// emulate disabled option

function emulateOption() {
  if (element.disabled) {
    element.style.color = "graytext";
  } else {
    element.style.color = "menutext";
  }
}

//]]>
</SCRIPT>
</PUBLIC:COMPONENT>
Above is a core HTC file which defines the DHTML Behaviors for SELECT and OPTION. What it does are:
  • onDocumentReady, onDetach
    Initialization and cleaning up.
  • onChangeSelect
    When a selection is changed for a SELECT, check if the selected option is disabled. If it is disabled, revert the selection. If it is not disabled, save the selectedIndex for later use, and run the original onchange event handler set by user.
  • onPropertyChangeSelect
    When a onchange event handler is set or changed for a SELECT, save the event handler for later use and set the onchange to null. This ensures that onChangeSelect above is called, which then calls the onchange event handler set by the user. This mechanism is needed because, by the spec, a onchange event handler set in a HTML file is called before a onchange event handler in a HTC file. This makes it possible to work consistently when there is a onchange event handler in the HTML or there is JavaScript/DOM manipulations.
    Contributed by Zecc:
    When a selectedindex value is set or changed for a SELECT, save the value for later use. This makes it possible to work consistently when there is JavaScript/DOM manipulations.
  • onPropertyChangeOption
    When a disabled attribute is changed for a OPTION, change the text color of the OPTION, so it will look enabled or disabled. This makes it possible to work consistently when there is JavaScript/DOM manipulations.

Download

You can download zipped files from here.
Download includes sample-javascript.html which is a little more complicated version of sample.html, to demonstrate how the solution works when there are some JavaScript/DOM manipulations.
Note that if you open HTML files from a local directory, IE will display a alert, but this will not happen when you open the files from a web server.

Copyright?

This solution is dedicated to the public domain.
posted by apptaro at 11:32 | Comment(39) | TrackBack(0) | HTML
Comments
This is great. It helped me a lot.

I do have one minor suggestion. When handling properties changes for the Select, you should also handle changes to selectedIndex. This is to prevent unproper behaviour after you assign it programmatically.

===

function onPropertyChangeSelect() {
var sChangedPropertyName = event.propertyName.toLowerCase();
if (sChangedPropertyName == "onchange") {
hackOnChange();
}
else if (sChangedPropertyName == "selectedindex") {
nLastSelectedIndex = element.selectedIndex;
}
}

===

Thanks for the code. I'm assuming you are releasing it as public domain?
Posted by Zecc at 2006/10/18 20:29
Thank you for the suggestion, Zecc!

Yes, it's public domian. Feel free to use it. Is your suggestion also public domain? Assuming so, I have merged your code, so other people can benefit from it.
Posted by apptaro at 2006/10/19 13:10
Thank you so much, I have been looking for a solution to this problem for some time.
Much appreciated
Posted by Mr F at 2007/05/21 19:59
Quick note just by adding a few lines the options that are selected='selected' option will also work which is a problem in ie

it is a very quick and dirty addition

function onPropertyChangeOption() {
var sChangedPropertyName = event.propertyName.toLowerCase();
if (sChangedPropertyName == "disabled") {
emulateOption();
}
else if (sChangedPropertyName == "selected") {
emulateOption2();
}
}



function emulateOption2() {
if (element.selected) {
element.style.color = "gray";
} else {
element.style.color = "menutext";
}
}
Posted by Mr F at 2007/05/21 21:20
what about Safari?
Posted by ilia at 2007/05/22 01:55
Safari 3 fixes this problem.

Posted by Mr K at 2007/06/26 04:21
Thanks a lot for these 2 reasons :
1 - You (almost) saved my (customers) life !
2 - I have discover DHTML bevaviors (sweeet...)
Posted by hadrien at 2007/09/25 23:31
Thnx ALOT!!!!!
You saved my day..., not to say a week!

Posted by per at 2007/10/02 02:35
Please I need some help on how I can use sample.htc and sample.css with a php code to let selection disabled in IE.
Thanks in advance
Posted by Alven at 2007/10/05 06:39
A comment to Alven and others. May be just slightly off the topic, but it's something I've seen quite a bit.
I see people who ask how do I use a piece of javascript, or in this case an htc behavior file with PHP. When coding in PHP you need to think with an HTML mindset. If you had a regular HTML file that you were going to impliment this in, how would you do it.

Now back on track, as Apptaro added in the main post.

select, option {
behavior: url(sample.htc);
}

This can be added as CSS code in your HTNL file or as a separate included file.

SO then how do you put this into your PHP scripts. Add it in the same area that you would if you were adding it to a standard HTML file.

IF you view the source of the page generated by your script, you can probably figure it out very easily.
Posted by dbemowsk at 2007/10/12 03:41
Thanks for the code. I'm running into a problem when I specify an onchange handler in a tag attribute of a select element. The handler references the document object and causes an "Access is Denied" error. For example, it doesn't work when I do something like this:



Any idea what might be causing this?
Posted by hk at 2008/04/09 12:29
I think I found my problem. In hackOnChange(), element.onchange is saved into fOnChangeOriginal. That's okay but when we run the saved function later in onChangeSelect() we're not calling the fn through 'element' anymore so 'this' is no longer what the author of onchange expected.

If we replace fOnChangeOriginal() with fOnChangeOriginal.call(element), it works again. Please feel free to incorporate this change into your code.
Posted by hk at 2008/04/12 05:25
First of all, thanks for the solution :) It works great, but I think I found a little bug.

When using this script to disable selecting disabled options, it works fine. But when you add an onchange function to the select, that function is called with the wrong option selected. I made a little test with the onchange function just alert(this.value); (Used the suggestion provided by hk in the above post). I have an option with value 23 that is disabled. I select it, see the previous selected option being selected again but the alert shows 23.
It's not that big a deal for me, so I'm not going to suggest a solution for it as it requires too much work right now :)
Just letting you know it's there so you can fix it some day.
Posted by Caspar at 2008/04/16 18:18
Thank you, hk and Caspar, for your feedbacks, but I can't reproduce the issues. My test case is sample-javascript.html included in the zipped download file, which tests onchange="alert(this.selectedIndex)". OnChange is not called when selecting disabled option and thus reverted back to previously selected option. If you could please let me know non-working code example.
Posted by apptaro at 2008/04/17 12:40
I have updated sample-javascript.html to include some more tests.
Posted by apptaro at 2008/04/17 14:02
Looks wonderful, and (mostly) works well for me in a fairly complex Tomahawk JSF app. However in IE6 I get a harmless but unreleasable-to-the-customer error on invoking a page containing a select with disables that reads:

Line 97, char 1 (probably not your lines!)
Error: cannot set do-nothing method on that object onchange

code 0
(and my URL)

As I doubt you can reproduce in your code, I'm going to have to remove options instead of disabling ... too bad, but if you think of anything, it would be much better to use yours.
Posted by Robin at 2008/04/28 06:16
Robin, I actually use this with MyFaces Tomahawk and works fine. (Though, there actually is a performance problem when there are many selects on a page.) Do you know Microsoft Script Editor? I found it very helpful when debugging JavaScript errors.
http://www.jonathanboutelle.com/mt/archives/2006/01/howto_debug_jav.html
Posted by apptaro at 2008/04/28 13:09
Worked great, although I had to use hk's fix to the original item. BUt worked like a charm, it is now working in ALL browsers.

Thanks for the awesome idea!
Posted by JJ at 2008/04/29 22:47
Thank you a lot!!!! You save me a lot of time (now it's critical... as always :) ), and it's better solution then I wanna make anyway!
Posted by Maxceem at 2008/05/27 17:23
This works well, one problem though.

Table 1 Seat 1
Table 1 Seat 2
Table 1 Seat 3
Table 2 Seat 1
Table 2 Seat 2
Table 2 Seat 3


Notice the first option is disabled? In FF "Table 1 Seat 2" is automatically selected. In IE it defaults to "Table 1 Seat 1" despite this option being disabled.
Posted by Moginheden at 2008/08/22 00:24
hm... my code was cut out, replaceing < with {

{select name="seatingFORECAST?0" id="seatingFORECAST?0" onChange="updateSeat('seatingFORECAST?0')" } {option value="1:1" disabled }Table 1 Seat 1{/option}
{option value="1:2"}Table 1 Seat 2{/option}
{option value="1:3" disabled }Table 1 Seat 3{/option}
{option value="2:1"}Table 2 Seat 1{/option}
{option value="2:2"}Table 2 Seat 2{/option}
{option value="2:3"}Table 2 Seat 3{/option}
{/select}
Posted by Moginheden at 2008/08/22 00:26
Thanks for the awesome idea!
I'm running into a problem when I specify an onchange handler in a tag attribute of a select element. thx so mutch
Posted by covings at 2008/09/10 01:07
Very useful informations, nice tutorials!
thx for interesting reading :)
cheers
Posted by house clearance london at 2008/10/16 03:59
I'm able to use this successfully, but not in an AJAX context. I'm using ASP.NET 2.0, with .NET AJAX.

When a value in a dropdownlist with autopostback is selected, nothing happens. Setting a breakpoint in the code-behind reveals that no postback is ever generated. When disabling the css instruction, everything works as before, so this is clearly an issue with the behavior file.
Posted by Morten Grotan at 2008/10/22 20:11
Excellent method, but we have the exact same problem with .NET AJAX.

Any ideas, anyone?
Posted by Jesper Allermand at 2008/11/14 22:03
thanx to rao.
Posted by manu at 2009/02/21 15:26
Jupi! thank you very much! This IE bug will not be fixed in IE 8 :(
Posted by Audi at 2009/03/26 00:47
GREAT CLUESM INFORMATIVE ARTICLE, THX
Posted by kinotakara at 2009/05/05 00:36
Excellent
Posted by dragan misita at 2009/05/28 04:50
A simple and elegant solution that works great! Thank you very much for the solution and posting it for everyone to use!
Posted by Eastside at 2009/07/15 02:28
It worked like a wonder; using it with JSF 1.1 Sun RI and Richfaces. Two issues we are facing:
1. Performance bottleneck when there are a lot of dropdowns.
2. An a4j onchange event is not being triggered when in a different form within the same page. It was working fine without this behavior.

Thanks.
Posted by Sudhin Chandy at 2009/08/04 08:18
I have a form with multiple selectbox.
I should like implement only on one select box i try to chage style like this but this don't work
select#nstatus, option#nstatus {
behavior: url(sample.htc);
}
Thnks
Posted by Calmette at 2009/11/10 19:48
Thx a lot, great !
Posted by cinv at 2010/01/06 19:22
wunderbaum!
Posted by baterie słoneczne at 2010/09/05 23:52
Thanks a lot!
Great hack!
Posted by Clausia at 2010/10/21 05:43
REally good stuff. Amazing what we can do with CSS. I like it!
Posted by leather furniture at 2011/05/13 17:21
それぞれの冬は、ダウンコート、リストの作品を購入しなければならないいくつかの並べ替えする必要があります!この翌年、彼はもはや個々の商品に入れて自己の快適決定することを余儀なくされることなく、バーバリーシャツコートが真になっている下のすべての方法では、メインキャラクターの好感!コンポーネント上のアカウントには、右にもっと方法です!

Javaの合計、他のカーディガンダウン大きい、また、モンクレールジャケットGammeルージュ、さらにリリースで実際のヒョウ柄も、この年の周辺写真と焼けるように暑いタータンで生産されますが、バーバリー単にホットはるばる髪のブーツと一緒にカーディガンダウンと一緒に彼らならば、信じられないほど快適で人に向かって、それはそう目を引く。

薄いハーネスファッショナブル、バーバリープローサム、衣装ほぼすべてのスリップと冬のシーズンは、トリム胃のいくつかの並べ替えが出て来ました。ストレートダウンジャンパー、バーバリー コート レディースより曲線美長ったらしいに行く。また、ストレートカーディガンファッションペンスカートダウン、また襞飾り付きのコートの短いセクション内で、エミュレーションを持つことが非常に当為があり、また、英語のガソリン冬期に接続規制にもかかわらず、非常にエレガントであります。

ピーターPilottoは短いのではなく3次元効果のRuプリントに専念どんな厚いジャケット機能と同じように、季節の特別な形状の極めて正常な感覚で絶望感を発揮する排他的なかなり大きな設計低いコート、バーバリー ショルダーバッグ画像上下のカーディガンを分離で使用されているコレクションのいくつかの並べ替え。 Rundletまた、トレンディ、まだ装飾が置かれ、それは、成分が最も人気の9月であることです。
Posted by バーバリーバッグ,バーバリーシャツ,バーバリー at 2012/11/07 12:26
ديكورات
ديكورات جبس
Decoration
home decoration
decor
ديكور
كرانيش جبس


http://www.betcomegypt.com/home.php
Posted by Betcom Decoration at 2013/12/08 00:43
I really like your writing style, good information, thankyou for posting D. kddgegegfdea
Posted by Pharmd938 at 2014/04/24 04:35
Write Comments
Name: [Required]

Email:

HP:

Comments: [Required]

Code: [Required]


*Input characters in the image

Trackbacks