How to put a DIV over a SELECT in IE6?
Everybody who tried to implement an HTML menu knows it: in Internet Explorer 6, there are some elements like SELECT that always appear in front of all other elements no matter what z-index you apply to them. Other elements that present the same problem are ActiveX controls (including Flash movies), OBJECT tags, plug-ins and iFrames. That's because these elements are implemented as windows whereas other elements are just drawn on the existing background window.
<rant>This sucks. Really. It's a typical case where an implementation detail forces web developers to go through hoops. This problem is IE only and everything works fine in all other modern browsers.</rant>
Now, is it possible to work around it? There is always the possibility of a getElementByTagName and of hiding all such elements from the page when showing an overlaid DIV. Uck. That would really puzzle your users even though it doesn't really impair usability very much in the case of a menu which will go away as soon as you take the mouse cursor out.
There is actually a much better workaround, and that's what the new ASP.NET menu is using. I suspect that most other professional menu controls do the same. Let me warn you that it's very hacky and takes advantage of a behaviour that seems as buggy as what we're trying to fix, but it's also very efficient and seamless.
The idea is to put an iFrame (which is also a windowed control) on the page at exactly the same location as your DIV. The iFrame must be added at the end of the page so that it appears in front of all other windowed controls (the windowed controls are stacked in the order in which they appear on the page). That takes care of covering any SELECT that may be in our way. Now, you may wonder what good that will achieve as I'll just cover my DIV with an iFrame instead of with a SELECT: that shouldn't buy me anything. Well, for some strange reason, it does. IE seems to be utterly confused by our little trick and just shows the DIV in front of the iframe if and only if you position the DIV after you've positioned the iFrame. It seems to forget about the windowed nature of the iFrame for what it renders after it.
If you do it using javascript, just set the position of your DIV after you've set-up the iFrame. If you do it in the HTML markup, just define the DIV after the iFrame. Here's HTML markup that successfully displays a DIV in front of a SELECT element:
<select>
<option>This usually appears on top in IE</option>
</select>
<iframe src="javascript:'<html></html>';" scrolling="no" frameborder="0"
style="position:absolute;width:50px;height:120px;top:0px;left:0px;border:none;display:block;z-index:0"></iframe>
<div style="position:absolute;width:50px;height:120px;top:0px;left:0px;border:solid 1px black;z-index:0">
This appears in front of the select in IE
</div>
<option>This usually appears on top in IE</option>
</select>
<iframe src="javascript:'<html></html>';" scrolling="no" frameborder="0"
style="position:absolute;width:50px;height:120px;top:0px;left:0px;border:none;display:block;z-index:0"></iframe>
<div style="position:absolute;width:50px;height:120px;top:0px;left:0px;border:solid 1px black;z-index:0">
This appears in front of the select in IE
</div>
One thing is worth noting: if you work with a secure site (with an https: protocol), you can't use "about:blank" as the url of the iFrame, otherwise you'll get a warning from IE saying that the page uses both secure and insecure contents. Not very nice to your users. So in this case, you'll just need to point the iFrame to some blank but secure page on your site. That's an extra hit to the server but hey, you already have a hit for each image on your site and that doesn't prevent anyone from having dozens of images on each page. Only in this case, it won't get cached client-side. See update below for a workaround...
Update: using "javascript:;" as the src of the iFrame in the https: case does the trick without the additional hit to the server. Thanks, Scott.
Update to the update: "javscript:;" is fine if your div is not transparent, but it will display an error message on the iFrame, so it should be avoided for transparent divs. By the way, if your div is transparent, you need to make the iFrame itself completely transparent (using an Alpha filter). That won't affect the hiding power of the iFrame (selects will actually not show through the div).
Update to the update to the update: thanks to David Anson and Kirti Deshpande who pointed me to this neat trick: using "j avascript:'<html></html>';" works well in all cases, even transparency, and avoids the https alert.
LAST UPDATE to this post: Folks, it might be a good time to stop and consider how much it costs you to support IE6. This hack to hide selects is only necessary for older versions of IE that are shrinking fast (6 or smaller; 7 and 8 are NOT affected). I actually think that it is now hurting more than helping to make your site look better in IE6. Give them a reason to upgrade, and show them how. That may be a better use of your time... Just an opinion, but please consider it.