Large Hit State Menus with Javascript and CSS

A growing trend I have been noticing while developing web sites is the need (or want) to create areas on the page that are clickable. Generally these are areas that contain a headline and a snippet of text, and sometimes an image. I really like the idea of having areas of that page that are clickable because I believe it encourages users to click through right away, and can make those worn out ‘click here’ links obsolete.

Typically these large hit state links are created using images. This is both good and bad. Since it’s an image, we can control how the area looks very easily. But the bad thing is it doesn’t do you any good for accessibility and search engine visibility.

Good hit states should:

  • Allow a user to click anywhere in the area to follow the link;
  • Visibly cue the person that the area is a link;
  • Degrade gracefully to users without CSS or Javascript enabled; and finally
  • Integrate semantically into the document to make sense in the structure.

In this example we’re going to create a navigation menu for a web site. Our customer wants to have menu items that have a title and a small description. The designer wants the whole thing to be a link. Here’s how we pull it off.

Step One: Set up the menu

Typically while creating navigation menus using CSS, I tend to use unordered lists. For this example, it won’t work very well because there will be elements in each menu item that won’t work inside a li tag. To keep the document semantically correct, I want to use something like an h4 element for the title and paragraph tag for the description. Embedding these in a li element is technically valid HTML, but it’s a bad way to structure a document. I would even go so far as to say it’s similar to embedding structure inside nested tables. So we’re going to create a menu items out of div elements containing a headline and a paragraph element.

  <div class="menu_item">
  <h4><a href="index.html">Home</a></h4>
  <p>Go to the home page</p>
  </div>
  <div class="menu_item">
  <h4><a href="about.html">About</a></h4>
  <p>Read about how rad we are</p>
  </div>
  <div class="menu_item">
  <h4><a href="http://www.myfriendssite.com" rel="external">My Friend</a></h4>
  <p>My Friend's Site</p>
  </div>

That was simple, clean and valid HTML. Using h4 tags denotes importance, but not enough importance on the page to overrule your headlines. We have a short description in there with a paragraph tag. Notice we add a menu_item class to each div element. This is how we’ll work the magic later with Javascript.

Step Two: Set up the CSS

Now we’ll write some basic CSS to style the menu. Of course you’ll want to style it differently, so I’ll keep it super simple.

  div.menu_item {
    border: 1px solid #CCC;
    padding: 10px;
    width: 100px;
  }

Each menu item will be 180 pixels with a border and 10 pixels padding. Now let’s add a class for a hover state of the menu item. We’ll turn the background a slight gray and make the border darker. Then we’ll change the cursor to the pointer icon to make it appear to be a link.

  div.hover {
    background: #CCC;
    border: 1px solid #999;
    cursor: pointer;
  }

If Internet Explorer 6 played nice we’d add a :hover pseudo-class to the menu item in the CSS and be done. However, it doesn’t support CSS hover states on anything but links. Internet Explorer 7 in strict standards compliant mode does, but it’s currently a smaller subset of browser share. So we’ll need a little help from Javascript.

Step Three: Set up the Javascript

As with most of my Javascript snippets, you’ll need Prototype included in your page. For those just wanting to copy and paste the snippet and be done, I’ll throw it out now:

  function init_menu() {
    var items = document.getElementsByClassName('menu_item');
    items.each(function(item) {
      Event.observe(item,'mouseover',function() {
        Element.addClassName(this,'hover');
      }.bind(item));
      Event.observe(item,'mouseout',function() {
        Element.removeClassName(this,'hover');
      }.bind(item));
      Event.observe(item,'click',function() {
        var link = this.getElementsByTagName('a')[0];
        if (link.getAttribute('rel') == 'external') {
          window.open(link.href);
        } else {
          window.location = link.href;
        }
      }.bind(item));
    });
  }
  Event.observe(window,'load',init_menu);

For those who are still reading and want to know how it works, let’s go through it a few lines at a time.

How it works

First we get all the items in the document with the class name menu_item.

  var items = document.getElementsByClassName('menu_item');

Next we loop through each item found and apply event listeners to each one. Instead of the typical for (var i = 0; i < items.length; i++) pattern, we use Prototype’s each iterator, which cleans things up a lot in our code.

  items.each(function(item) {

The first thing we want to do to our menu item is apply a mouseover and mouseout state to it. We’ll add event listeners for the element to apply the hover CSS class to change the appearance. Since Prototype comes with a built in addClassName and removeClassName functionality, as well as event listeners using Event.observe, we don’t have to worry about supporting how it works across multiple browsers. Prototype handles that for us.

  Event.observe(item,'mouseover',function() {
    Element.addClassName(this,'hover');
  }.bind(item));
  Event.observe(item,'mouseout',function() {
      Element.removeClassName(this,'hover');
  }.bind(item));

Notice the bind call at the end of each even listener we set up. This allows us to reference the item as this inside the function block we pass into the event listener call. Otherwise it wouldn’t know what item you were referring to.

Now you might be thinking ‘how do you turn to the whole thing into a link when the link is only in the headline?’. Here’s how we do it: we parse the menu item and find the first link, then make the menu item listen for a click. When it’s clicked, it will open the link as though the real link inside the menu item had been clicked.

  Event.observe(item,'click',function() {
    var link = this.getElementsByTagName('a')[0];
    if (link.getAttribute('rel') == 'external') {
      window.open(link.href);
    } else {
      window.location = link.href;
    }
  }.bind(item));

It’s even set up to check for the rel="external" attribute of a link (covered in my previous post) and will open it in a new window if it’s present.

Finally we’ll set up an event listener to call the function after the page is loaded:

  Event.observe(window,'load',init_menu);

Stick this in an external Javascript file on your page or into the head of your document, and your menu items that can contain anything you want, and still appear as one large link. You can also apply it to more than than just menu items: use it on special callouts on your pages, such as download links, lists of previous blog posts, or product ads.

Users without Javascript enabled will still be able to view and click on links inside the menu items, they just won’t be as stylized as when Javascript is enabled. Search engines will also love your h4 headlines and description for each link, so your site could possibly rank for more relevant searches. What’s not to like?

I’ve uploaded a very basic example so you can see it in action. If you found this useful and applied it to your site, leave a comment and link to where you used it.

0 Responses to “Large Hit State Menus with Javascript and CSS”


  1. No Comments

Leave a Reply