Skip menu

Hi there!

This script is from 2003, and targets a flaw in IE6, which at its peak was a really popuplar browser. Today (oct 2011) it no longer is, and as such this script should not really be necessary anymore.

Please try to convice your client, project manager or boss, that not every browser needs to display a website the exact same way; it is the content that matters. And if you really still need to support IE6, don't use :hover for interaction.

This page (and script) will stay here for old times' sake.

updated: november 15, 2009 (changes) Whatever:hover 3.11

Most modern browsers support the :hover selector for any html element. This is cool, because it enables you to, for instance, apply a mouseover effect to table rows <tr> using only CSS. IE however, has an erratic support for :hover at best, depending on the particular version your visitor is using.

Whatever:hover is a small script that automatically patches :hover, :active and :focus for IE6, IE7 and IE8 quirks, letting you use them like you would in any other browser. Version 3 introduces ajax support, meaning that any html that gets inserted into the document via javascript will also trigger :hover, :active and :focus styles in IE.

If you're already familiar with whatever:hover and just want to download it, scroll down and grab the latest version. For some in depth stuff on the other hand, just continue reading.

Getting it to work How do I use it?

Link whatever:hover to the body element, and you're all set. Note that behavior URLs are relative to the html file, not to the CSS file like a background image URL would be.

body { behavior: url("csshover3.htc"); }

IE only behaviours How does it work?

All browsers provide some way to look at a stylesheet's rules using javascript, and dynamically insert new rules. Normally, IE returns "unknown" for anything it does not support, for instance; a "div p:first-child" would change into "div p:unknown", and a "p a[href]" would be returned as "UNKNOWN" altogether. Fortunately IE recognizes :hover as something it's familiar with, and leaves it alone.

IE also supports so called behaviors; both predefined functionality like dynamic content loading or persistent data storage, as well as custom behaviors that you can build into an .htc or .hta file. These behaviors are linked to html nodes via css, and "enhance" the nodes that are selected by a rule's selector with the given behavior.

Combining the above, it should be possible to create a bahavior that searches the styles for rules IE doens't support, and trick affected elements into applying the associated styles some other way. The steps involved in this are something like:

  • Search all stylesheets for :hover rules IE doesn't support,
  • Insert a new rule IE does support, like one with class names,
  • and finally, set up script events for switching class names.

This way, :hover, :active and :focus can be supported, and as a developer you don't have to do anything except including the behavior. Everything else runs on full automatic.

Unlike versions 1 and 2, version 3 also supports dynamically added html (ajax). The difference is that 1 and 2 actively searched for affected elements onload of the page (so; only once), whereas 3 uses expressions to let nodes do a callback themselves. Read the commented version for more details on this.

for a menu An example of :hover

A common use for :hover is creating menusystems using lists. A dual level menusystem is almost too easy to create using this behavior. For instance, if you would remove the javascript from the suckerfish dropdown, described in the A list apart article and add this behavior to the body, it will work.

Multiple level menus require a different approach though. This is because IE lacks support for the > child selector, which would be perfect to show direct submenus, and not yet the deeper nested ones:

li:hover > ul { /* no go in IE */ }

There is an alternative way to simulate this, using simple descendant selectors only. A previously described method was based on the use of extra classnames, but an easier way is to use the specificity of CSS. Every CSS rule has a certain importance, which can be determined by simply counting the different elements of a rule. Nodenames count as "1", class, pseuo-class and attribute selectors count as "10", and ids as "100". Take the example below.

ul ul { display:none; } li:hover ul { display:block; }

The reason that this works, is because of the specificity. The first rule contains only 2 nodenames, which makes its value 2. The second also contains 2 nodenames, but the :hover pseudo class is worth 10, so the combined value of the rule is 12. Because 12 exceeds 2, a hover on the li will show the ul (or rather all of them) nested within.

So how does this help getting around the > child selector? Well, if a rule of 12 would show all submenu's, all we would have to do is make a rule that is worth more than 12 to hide the next menus. Subsequently, that menu would have to be shown by another rule, again worth even more, and so on. For 3 levels of navigation, the CSS code is surprisingly short:

/* 2 and 13 */ ul ul, li:hover ul ul { display:none; } /* 12 and 23*/ li:hover ul, li:hover li:hover ul { display:block; }

This way (and adding more for 4 or more levels) unlimited nesting of menu's can be used, without ever needing extra classnames.

script events mayhem Optimizing

There is just one more thing to consider. The .htc file searches the stylesheet for :hover rules, and attaches a mouseover and mouseout event to elements that in its opinion require a scripted hover effect to work as the css file says. To find those elements, everything after (and including) the :hover is trimmed from the selector, and the resulting elements are selected and processed. A rule like:

#menu li:hover ul { ... }

... would be trimmed back to this to find all the elements that might need hover events:

#menu li { ... }

Obviously this would select every single <li> element in the entire menu, attaching events to heaps of elements that don't necessarily need them (in this case). This could easily be addressed by a classname only for list-items that contain submenus, for instance "folder". In that case the trimmed line would read:

#menu li.folder { ... }

... effectively selecting only those elements that actually need events. The downside is that you'd be using a classname to get the best performance from the script (disregarding menu-wide li:hovers for purely visual effects), on the other hand you might have used a class anyway to tell these list-items apart from normal items.

To illustrate all this; take a look at the combined example. Enjoy.

updated: november 15, 2009 Files, notes & changes

Version 3 Files:

Version 3.11 (:hover, :active and :focus)
Minified, 2.8K (right click & save) | commented, 9.7K | both, zipped


If you run in to issues with whatever:hover, let me know! :) Version 3.10 fixes the continuous exression callbacks, so updating from 3.00 should give you an immense performance increase.

Make sure your webserver sends htc files with the mime-type set to text/x-component. For more info on this, check Aldo's blog. In case of an .htaccess file, it's:

AddType text/x-component .htc

Version 3 supports :hover and :active for IE6+, and additionally :focus for IE7 and IE8. Because expressions don't work in IE8 standards mode, whatever:hover will only run on IE8 in quirks mode. It shouldn't be needed in IE8 standards anyway. CSSHover is free software, licensed under LGPL. You can use it both for commercial and non-commercial sites.


Older versions: (you shouldn't need these, really)

Version 1.42.060206 (:hover and :active) download | view
Version 2.02.060206 (1.42 and :focus) download | view


Version 3.11

  • Fixed a strange specificity issue.

Version 3.10

  • Fixed the continuous expression calls.
  • Got rid of the date in de the version.

Version 3.00.081222

  • Minor change to the unload function.

Version 3.00.081221

  • Pretty much all new from the ground up.
  • Added ajax support.

Version 1.42.060206 / 2.02.060206

  • Fixed a bug where a non existing #id would kill the script
  • Fixed lack of support for .classes preceding :focus (d'oh)

Version 1.41.050927 / 2.01.050927

  • Fixed :active, it actually didn't work.
  • Added :focus in a separate 2.01 version. No other differences from 1.41.
  • Added an onunload handler to clean up the created events, this fixes an increase in both parsetime and memory use per reload.
  • Added a check to prevent the script from working in IE7 (including any alphas and betas), since :hover will work natively.
  • Both 1.41 and 2.01 are now licensed under LGPL. Yes, this allows free commercial use.
  • Various other minor tweaks.

Version 1.30.050121

  • Specifying nodenames is now really no longer required (still was in 5.x)
  • Added a security check for stylesheets from other domains.

Version 1.21.041022

  • Fixed a weird class bug where the hover wouldn't work.
  • Fixed script error for empty rules.

Version 1.20.040823

  • Added support for :active.
  • Fixed an obscure case sensitivity bug.

Version 1.11.040203

  • @import url(); is now fully supported.
  • Specifying nodenames in CSS rules is no longer required.
  • Dynamically created rules are added to originating stylesheet now.
    • This fixes any media or sheet-switching bugs that might have existed.
    • And it fixes a bug where relative paths were parsed incorrectly for url();s.
  • Various other internal fixes and changes.

Version 1.00.031224

  • Original version

Naar Voren, a Dutch site about webdevelopment and every aspect of it, features a more lengthy article (in Dutch, by me) about the use of :hover in menusystems using only CSS. For those who don't know Dutch, an English translation has been written.

Thanks go out to Arnoud Berendsen and Martin Reurings for ideas and support, to Robert Jan Verkade and the guys from Naar Voren for publishing my article, and to Eric Meyer for supporting this script and mentioning this page in his book.