Disable Select Options in Internet Explorer

Put your hands up if you hate developing for Internet explorer….

That would be most of you then.

I can’t stand its special ways of doing things, so when I find an easy work around it makes me happy.

The latest one I have had to find and put into practice is a method for disabling some Select dropdown options in a form. Internet explorer doesn’t support the disabled attribute on option elements. The following code will only work in standards complaint browsers (not in IE 6 or 7):

<select id="select_1" name="a_select">
	<option>Please select...</option>
	<option disabled="disabled">----------</option>
	<option>One</option>
	<option disabled="disabled">Two</option>
	<option>Three</option>
</select>

But there is a simple(ish) workaround we can use. Replace the disabled option elements with empty optgroup elements. This looks good in all browsers except for Safari which won’t show the option using the below code:

<select id="select_1" name="a_select">
	<option>Please select...</option>
	<optgroup label="----------"></optgroup>
	<option>One</option>
	<optgroup label="Two"></optgroup>
	<option>Three</option>
</select>

By adding a little css styling magic and you end up with an identical outcome in all other modern browsers.

You can then enable and disable using javascript. Many people have written code which makes an option look like it’s disabled, waits for a click on the option element and then bluring it or focusing the next / previously selected option to make it act like it’s disabled.

I have come up with functions used with jQuery to disable / enable a select option by converting it to an optgroup and back. It is tested in firefox 2 & 3, safari 3, ie 6 + 7, and it works in Opera 9 (although the opgroups jump to the bottom)

The code

$(document).ready(function()
{
	$.fn.extend(
	{
		optionDisable:function()
		{
			var ths = $(this);
			if(ths.attr('tagName').toLowerCase() == 'option')
			{
				ths.before($('<optgroup>&nbsp;</optgroup>').css({color:'#ccc',height:ths.height()}).attr({id:ths.attr('value'),label:ths.text()})).remove();
			}
			return ths;
		},
		optionEnable:function()
		{
			var ths = $(this);
			var tag = ths.attr('tagName').toLowerCase();
			if(tag == 'option')
			{
				ths.removeAttr('disabled');
			}
			else if(tag == 'optgroup')
			{
				ths.before($('<option />').attr({value:ths.attr('id')}).text(ths.attr('label'))).remove();
			}
			return ths;
		}
	});
});

Fixes and explainations

Safari requires a &nbsp; in the optgroup tag to make it visible, and because of this Firefox 2 then required the height:ths.height() to stop it showing a blank line below the optgroup.
Two small fixes.

Additionally (as kindly pointed out by Luenyar below) this will not work if the options you are playing around with are already in an optgroup. That said, it will work but will create unwanted optgroups.

I almost forgot to mention that the functions are chainable!

Example

And to put it into practice you could use the following code snippets:

/* a specific option */
$('#a_select').children(':eq(2)').optionDisable();
 
/* the selected option */
$('#a_select option:selected').optionDisable().text('I am a disabled option!');
 
/* enable all options in a select */
$('#a_select').children().optionEnable();

Update: Be sure to read all the excellent comments for further thoughts and improvements

24 comments.

  1. Simply awesome, exactly what I was looking for.

  2. This concept is great. I couldn’t get this to work as it is written, probably since I’ve already got stuff I want ‘disabled’ in an optgroup and you can’t nest them, plus I personally won’t be enabling/disabling from the form. So I came up with something a bit different that suits my needs. I’m calling this in a js that is included site-wide.

    Basically this code finds all the child options of an optgroup labelled ‘inactive’, inserts new options outside in the parent select object, then empties the optgroup. Dunno if the code will format right in a comment but here goes a paste:

    $(document).ready(function() {
    	var inactiveGroup = $('optgroup[label="Inactive"]');
    	if ($.browser.msie) {
    		inactiveGroup.children('option').each(function() {
    			theOption = $(this);
    			theOption.parents('select').append( $('&nbsp;').css( {color:'#CCCCCC',height:theOption.height()} ).attr( {label:' '+theOption.text()} ) );
    		});
    		inactiveGroup.empty();
    	} else {
    		inactiveGroup.attr({'disabled':'disabled'});
    	}
    });
  3. Part of the above got escaped out, much like the OP. Possibly what caused me my confusion. The line:

    theOption.parents(’select’).append( $(’ ’).

    should not be just empty quotes. Inside the (‘ ‘) should be HTML tags for an optgroup and a non-breaking space.

    Hope that helps someone.

  4. Thanks for your post Luenyar and a great catch. I really don’t know when the html tags got dropped from my code, but thanks for pointing it out.

    Also thank you for noting that this won’t work for options already in an optgroup. I have added a not to the original post.

  5. I searched high and low for a solution to the .attr(“disabled”,”disabled”) bug in IE. Nothing substantial surfaced except this. Thanks!

  6. One note, I couldn’t get the optionEnable function to work. Have you tested it?

  7. Just tested it again now, as it appears above and I can confirm it works in Firefox, Safari, IE6

  8. I still see a blank line in firefox 3 if the disabled option is the last one. I added a check of jQuery.browser to look for safari instead.

  9. Hey Harry:

    Thanks for this! I’ve spent an afternoon trying to programatically enable/disable options in IE. Your code has gotten me closer than I would’ve been able to get on my own.

    One issue I’m hoping you can help with. In my function, I want the first line to enable all options. This way, only those that need to be disabled for a given action (triggered by a radio button in a group) can be subsequently disabled.

    So, the scenario would be that on page load, all options are enabled. A specific radio button is clicked, which would disable 1, and 4 (for example). Now, another radio button is checked that should disable 2 and 3 (but 1 and 4 should be enabled).

    I tried $(‘#mySelect’).children().optionEnable(), but it doesn’t work as expected. On the first click, 1 and 4 disable. But on the 2nd click, they’re still disabled. I’m thinking it’s because of the insertion of the means the previously disabled options are no longer children of the select element (bear with me, I’m a jQuery n00b).

    Tried a few workarounds on my own, but can’t seem to “reset” all of the options to be enabled again. If you have any thoughts, I’d appreciate it!

    Thanks,
    Charlie

  10. Hi Harry:

    Nevermind. Got it working. But it wouldn’t work with the following:

    $(‘#mySelect’).children().optionEnable();

    I had to do add an each():
    $(‘#woActionSelect’).children().each(function() {
    $(this).optionEnable();
    });

    Not sure why, but it works, so I’m happy :)

    Thanks for making this available!

    Charlie

  11. Hi Charlie,

    Thanks for the comments and bug. I will look into it when I get a chance and probably update the code.

    It should certainly work without the each.

    Harry

  12. Hi Harry:

    I agree, it should work without the each(). Not sure what’s going on. Altho, as long as it works, I’m a happy person :)

    If it helps any, we’re running jQuery 1.3.1 and IE 7.0.5730.13.

    Thanks!
    Charlie

  13. Did anyone discover how to get rid of the bold italics look of the optgroup-element?

  14. @Charlie

    You can do so with css for all browsers except for ie6.

    With ie6 I’m afraid you are stuck with italic black bold as it ignores css for optgroup.

  15. This is fantastic, its exactly what I was looking for. I ran into the “each” problem as well, but modified the code to work by wrapping the innards of the optionDisable and optionEnable functions in their own “each” function. (No idea if this will format correctly, but here goes…)

    $(document).ready(function()
    {
    	$.fn.extend(
    	{
    		optionDisable:function()
    		{
    			var opts = $(this);
    			opts.each(function () 
    			{
    				var ths = $(this);
    				if(ths.attr('tagName').toLowerCase() == 'option')
    				{
    					ths.before($('&nbsp;').css({color:'#ccc',height:ths.height()}).attr({id:ths.attr('value'),label:ths.text()})).remove();
    				}
    				return ths;
    			});
    			return opts;
    		},
    		optionEnable:function()
    		{
    			var opts = $(this);
    			opts.each(function () 
    			{
    				var ths = $(this);
    				var tag = ths.attr('tagName').toLowerCase();
    				if(tag == 'option')
    				{
    					ths.removeAttr('disabled');
    				}
    				else if(tag == 'optgroup')
    				{
    					ths.before($('<option/>').attr({value:ths.attr('id')}).text(ths.attr('label'))).remove();
    				}
    				return ths;
    			});
    			return opts;
    		}
    	});
    });
  16. Exactly what I was looking for. You saved my sanity! ;)

  17. small note I just ran into when disabling and enabling items in Internet Explorer you end up returning a height of zero when converting an option into and optgroup. This for whatever reason causes IE to render the optgroup as an option. Very annoying! to fix it remove “,height:ths.height()” when creating the optgroup in the code above. Hope this saves someone from the same headache I have ;)

  18. .

    ieLessThan8OptionDisable = function() {
    	if ($.browser.msie && parseFloat($.browser.version) < 8) {
    		$("select").find(":disabled").addClass("disabled").removeAttr("disabled");
    		$("select").change(function(){
        		var selected = $(this).val();
        		var disabled = $(this).find("[value="+selected+"]").hasClass("disabled");
        		if (disabled) {
        			alert("This option is disabled.\nSelect will be set to the first option.");
        			$(this).find("option:first").attr("selected","selected");
        		}
    		});
        }
    }
  19. Thanks for your comment Brant, but your method is much less friendly for users.
    If multiple options are disabled then users are going to see lots of alerts popping up and generally give up filling in your form.

  20. Thanks Harry, this worked out well for me. However, I didn’t agree with changing the option’s value attribute into the id attribute of the optgroup… (in case I had common values like 0,1,2,3…) so I used the “alt” attribute:

    $.fn.extend(
    	{
    		optionDisable:function()
    		{
    			var ths = $(this);
    			if(ths.attr('tagName').toLowerCase() == 'option')
    			{
    				ths.before($('&nbsp;').css({color:'#ccc',height:ths.height()}).attr({alt:ths.attr('value'),label:ths.text()})).remove();
    			}
    			return ths;
    		},
    		optionEnable:function()
    		{
    			var ths = $(this);
    			var tag = ths.attr('tagName').toLowerCase();
    			if(tag == 'option')
    			{
    				ths.removeAttr('disabled');
    			}
    			else if(tag == 'optgroup')
    			{
    				ths.before($('<option/>').attr({value:ths.attr('alt')}).text(ths.attr('label'))).remove();
    			}
    			return ths;
    		}
    	});
  21. Thanks argg — we switched from JQuery 1.2 to 1.3 and that bug popped up for us as well.

  22. Good article.

  23. Great article and workaround for stubborn IE disable option.

  24. Hey guys is it necessary to put an optgroup tag ???