ShiftEleven

Mimicking the :hover Pseudo-class in IE

Internet Explorer frustrates the living hell out of me. Even though IE 7 is finally out, it doesn't mean that IE 6 can be ignored. I really look forward to the day when my CSS files aren't full of unnecessary classes and hacks because the major browser vendors got it right and because everyone has casted away the garbage. Until then, I am still in search of tricks to simplifying my life.

One of these tricks is to make IE pretend like it knows how to use the :hover pseudo-class on all tags.

If you have ever tried to do a suckerfish dropdown, then you have gone through the same thing -- excitement as you have learned how to use structural markup to create a dropdown and then frustration as your work has been nullified by IE. The fix involves creating duplicate selectors of those using :hover, but instead of using :hover, replace it with a class name .hover. Making what are essentially duplicates and managing more content doesn't sit well with me. The other trick to getting suckerfish to work in IE is to use JavaScript. The script will add a class definition to the tag when a user's mouse if over the element and then removes the class when the users mouse is no longer over it. Since this method uses JavaScript to solve one problem, why not make it solve both?

The Code

There is a requirement to using this code and that is you will need prototype 1.5.

var HoverBehavior = Class.create();
HoverBehavior.prototype = {
  initialize: function() {
    $A(document.styleSheets).each(function(stylesheet) {
      $A(stylesheet.rules).each( function(rule) {
        if( rule.selectorText.match(/:hover/i) ) {
          stylesheet.addRule( rule.selectorText.replace(/:hover/ig, '.hover'), rule.style.cssText );
        }
      });
    });

    $A(arguments).each(function(arg) {
      $$(arg).each( function(tag) {
        Event.observe(tag, 'mouseover', function() { Element.addClassName(tag, 'hover'); });
        Event.observe(tag, 'mouseout', function() { Element.removeClassName(tag, 'hover'); });
      });
    });
  }
};

In summary, it loops through all of the stylesheets, and for each stylesheet it loops through all of the rules. The first part of the initialization will only work in IE. When looping through the rules, IE uses the rules attribute while others use cssRules. Since this is only meant for IE, I am not going to bother making it cross-browser.

While looping through all of the rules, it is checking the selector text to see if :hover is in that text. If so it will append a new CSS rule to the stylesheet. The selector text for that rule is just like text of the matched selector; however, all instances of :hover have been replaced with .hover. The new rule's style is simply a copy of the matched selector.

That covers part 1 of the suckerfish fix, part 2 is to add the mouseover, mouseout states to the tags using :hover.

Usage

After the page has been loaded, then it is time to create a new instance of the HoverBehavior class. Since this is only for IE, it would be wise to use IE conditionals as that your other users, who are using complaint browsers, shouldn't have to process any unnecessary code.

<!--[if lt IE 7]>
<script type="text/javascript" charset="utf-8">
//<![CDATA[
  new HoverBehavior('li');
//]]>
</script>
<![endif]-->

The constructor can take any number of arguments that you would give to prototype's $$ function, which are like CSS selectors themselves. I could have also done something like:

The reason I don't assume the last one in that list is because I don't want to kill the user's browser. Instead of being lazy and choosing all of the elements, I only want to put the mouseover, mouseout functionality on the elements that need it.

Comments

comments powered by Disqus