December 19th, 2007 - by Paul OB

In this article we are going to re-visit a technique we first learned in the Star Matrix Pre-loaded article, except that this time we are going to use it to produce a navigation menu. Before we start it may be worth it to become familiar with the menu we are going to make; you can see a live version here.

At first glance it seems simple enough, but as often is the case there’s more to it than meets the eye. As you roll your mouse over a menu item all the menu items go into the off-state and only the hovered item stays alight. This is quite a subtle difference from the norm where just the item rolled-over changes. Usually this effect can only be done with added JavaScript but today you will learn how to do this with CSS alone.

Image Matrix

To pre-load the images we are going to use one big image that contains all the states that we need in our menu and then simply swap the background position to show the relevant portion of each image as required.

The first thing we need to do is create the image that we are going to use but we need to decide on what this image will be called to do.

  1. Create the menu items for the entire menu
  2. Allow for a default “on state” for the currently selected menu item
  3. Allow for all items to “switch off” on rollover except for the current item hovered
  4. Allow for the hovered item to be highlighted

I’ll be the first to admit that my graphic skills are zero so I have made probably the “cheesiest” menu you will ever see. I’m sure you can come up with something much better. As mentioned already we need to merge all these images into one image to create our image matrix. Here is a smaller version of the image that I have created (don’t laugh):

Figure 1
menu-fig1.png

It should be clear from the image above that we can cater for every state that we need to display. If you look at Figure 2 below you can see what the menu is going to look like when normal and what is going to happen when hovered.

Figure 2
menu-fig2.jpg

The top part of the image shows the normal state; then as soon as any part of the image is rolled over we switch all states off except for the currently hovered item as shown in the bottom part of the image.

You may think that the CSS to swap the images is relatively simple but you would be misguided. Although swapping images is easy you need to remember that there are in fact going to be seven anchors in our menu and each leading to a different page. When the mouse rolls over “link 1″ for example, we then need to change the background on all the other anchors also. This is quite challenging.

Z-index and Width Change on Hover Only

The way that this is going to be accomplished is to use absolute positioning for the anchors and then on hover we are going to change the width of the anchor to match the whole menu and lay a hovered version on top of everything else. There is a problem with this, if we overlay the whole menu with this one anchor then we loseWeight Exercise all the individual links and are stuck with just the current hovered item. This would have been a show stopper if there was no means to navigate to other links. We can get around this by decreasing the z-index of the anchor on hover which means that as soon as the mouse pointer moves off the current item then the anchors either side (which have a higher z-index) gain control of the focus and will apply their hovered images as required. These new images are again overlaid over the whole menu to continue the illusion.

This is quite a complicated process to grasp and although it sounds easy it is not intuitive and in fact doesn’t sound possible or likely to work. Luckily for us it does work – and works very well.

A Menu is a List of Links

So to make a start we need some HTML to work with and the ubiquitous list is ideal for the job. The following snippets of HTML that follow are not the full code and I suggest that you view the working example to grab the full code when you’ve finished reading the article.

Here is the basic HTML for the navigation menu.

  1. <ul id="nav">
  2. <li class="link1"><a href="menupage1.htm">Link 1</a></li>
  3. <li class="link2"><a href="menupage2.htm">Link 2</a></li>
  4. <li class="link3"><a href="menupage3.htm">Link 3</a></li>
  5. <li class="link4"><a href="menupage4.htm">Link 4</a></li>
  6. <li class="link5"><a href="menupage5.htm">Link 5</a></li>
  7. <li class="link6"><a href="menupage5.htm">Link 6</a></li>
  8. <li class="link7"><a href="menupage7.htm">Link 7</a></li>
  9. </ul>

The ul is given an ID of nav and I have also classed each list item for two reasons. First of all, I have made the menu items all a different size so I will need to give different widths to each menu item. Secondly, we also need to address each menu item in order to show the correct image for that item. Later on we will have a third use for the class and that will be to set the “current page” by using the class in the list in tandem with a body ID. This will allow for the menu to be used as an include and is the same technique used in the “current page indicator” article.

The HTML is pretty straight forward, it’s just a normal list with anchors inside. Now its time to get dirty with some CSS.

  1. ul#nav{
  2. list-style:none;
  3. margin:20px auto;
  4. padding:0;
  5. width:581px;
  6. height:46px;
  7. overflow:hidden;
  8. position:relative;
  9. background: url(images/menu-matrix2.png) no-repeat 0 -367px;
  10. }

The above CSS sets the width and height of the menu and also sets a stacking context using position:relative so that we have a base to place our absolute anchors from. (If you are unfamiliar with stacking contexts then refer to a previous article that explains in detail.) You will also notice that I have placed a background image in the ul. This is going to be the default menu that shows for the current page. The background-position is set at the correct value so that the home page menu item is lit and other items are inactive. This is easy to work out as I have used images that are 46px high and looking at the matrix you can just find the value for each row with simple mathematics.

The overflow:hidden is useful if you want to do some sort of text replacement and will stop any characters spilling out of the menu if text is resized.

So, initially we have no images applied to the anchors and the whole menu is one background image on the parent ul.

Absolutely Relative

The next CSS we need is for the lists and anchors and looks like this:

  1. ul#nav li {
  2. cursor: pointer;
  3. float:left;
  4. text-indent:-999em;/* basic text replacement */
  5. }
  6. #nav li,#nav li a {height:46px;}
  7. ul#nav li a {
  8. position:absolute;
  9. left:0;
  10. top:0;
  11. height:46px;
  12. text-decoration:none;
  13. z-index: 200;
  14. }
  15.  
  16. /* place individual links */
  17. ul#nav li.link1 a {left:0;width:69px}
  18. ul#nav li.link2 a {left:69px;width:104px;}
  19. ul#nav li.link3 a {left:173px;width:89px;}
  20. ul#nav li.link4 a {left:263px;width:73px;}
  21. ul#nav li.link5 a {left:335px;width:79px;}
  22. ul#nav li.link6 a {left:414px;width:73px;}
  23. ul#nav li.link7 a {left:487px;width:85px}

The list is floated and we have used a text-indent to hide the text that is in the anchor. Unfortunately this is not the best sort of image replacement and you would be better off using a method such as this. However that would over complicate things here so I leave that to your discretion to work out.

The anchors are then absolutely placed in the correct position and the positions are of course defined by their widths. If I’d been sensible I would have made them all the same width and saved myself some work. There is no need to worry about losing the flow of the document because the ul has a height and is not absolutely positioned and will therefore contain our absolute anchors without problem. Remember that we set position:relative on the ul which allows the anchors positioning to be taken from the ul and not the viewport.

An important part of the CSS here is to set the z-index of the anchor (only positioned elements can have a z-index). I have set it at 200 just to make a point but it really only needs to be one higher than anything else in the ul.

If you have made your image and used the above code you should now have a display that looks like this:

Figure 3
menu-fig3.png

All change on Hover

The next step is to create the hover styles so that when the menu is hovered the anchor will be stretched to cover the whole menu with an image that relates to the current state. We also need to change the z-index on hover so that the links on either side of a hovered item can get control back. The following CSS does just that:

  1. ul#nav li a:hover {
  2. z-index:2;
  3. width:581px;
  4. height:46px;
  5. overflow:hidden;
  6. left:0;
  7. background: url(images/menu-matrix2.png) no-repeat 0 0
  8. }
  9.  
  10. ul#nav li.link1 a:hover{background-position:0 0}
  11. ul#nav li.link2 a:hover{background-position:0 -46px}
  12. ul#nav li.link3 a:hover{background-position:0 -92px}
  13. ul#nav li.link4 a:hover{background-position:0 -138px}
  14. ul#nav li.link5 a:hover{background-position:0 -184px}
  15. ul#nav li.link6 a:hover{background-position:0 -230px}
  16. ul#nav li.link7 a:hover{background-position:0 -276px}

For each menu item’s the background position is adjusted accordingly to give the required state at that position. As mentioned earlier the background positions can be worked out from the original matrix and if you keep it uniform you can use equal steps for your numbers.

The effect is that on hover the z-index drops from 200 down to 2 only for the hovered item. As none of the other anchors have a background then our full length image still shows even though in effect it is under the adjacent anchors. As soon as you move off the current anchor the adjacent anchor activates its hover routine and displays its own image.

You should now have a basic working menu similar to that shown way back in Figure 2 and you can try it out here for real. (Don’t click the links as they don’t go anywhere yet.)

Where’s Your ID

The menu is in good working order but we are going to add the current page indicator as mentioned earlier and our first task is simply to add an ID to the body tag on each page of the menu. So for this first menu we will add an ID that says page1.

  1. <body id="page1">

On other pages you would add similar IDs until all menu items are catered for.

Now we need to address all the relevant links on each of those pages using the ID from the body in each page.

  1. /* Use body ID to set current item */
  2. #page1 ul#nav {background-position:0 -367px;}
  3. #page2 ul#nav {background-position:0 -413px;}
  4. #page3 ul#nav {background-position:0 -459px;}
  5. #page4 ul#nav {background-position:0 -505px;}
  6. #page5 ul#nav {background-position:0 -551px;}
  7. #page6 ul#nav {background-position:0 -597px;}
  8. #page7 ul#nav {background-position:0 -643px;}

The code above places an appropriate default image in the ul for each relevant page. The logic is quite simple and foolproof. Now when you visit each page the current item will be highlighted already.

Home Improvements

We are now going to change the function of the menu for a couple of reasons. First of all the menu in its current state of play allows for the currently active item to be clicked and the same page will re-load. This is bad practice and not something we really want to happen. Most included menus just ignore this fact and allow the same page to re-load which makes the user feel rather silly.

In order to prevent this we are going to place a span over the anchor so that the current link can’t be clicked. We will also negate the hover effect so that when the cursor is over the current item the menu returns to its normal state. Lastly and seeing as we are using an extra span we can take this opportunity to add a little arrow image to indicate the current item which will aid accessibility for those who are color blind (or who have been made color blind by my choice of colors).

The span is to be placed absolutely and the code is virtually the same that we used to place the anchors and could be amalgamated with comma separated selectors but I have left them split so that it’s easier to follow.

  1. ul#nav li span{
  2. position:absolute;
  3. height:46px;
  4. background:url(images/menu-uparrow.png) no-repeat 50% 100%;/* ie needs this*/
  5. left:-999em;
  6. top:0;
  7. z-index:999;
  8. width:69px;
  9. cursor:text;
  10. }
  11. #page1 ul#nav li.link1 span {left:0;width:69px}
  12. #page2 ul#nav li.link2 span {left:69px;width:104px;}
  13. #page3 ul#nav li.link3 span {left:173px;width:89px;}
  14. #page4 ul#nav li.link4 span {left:263px;width:73px;}
  15. #page5 ul#nav li.link5 span {left:335px;width:79px;}
  16. #page6 ul#nav li.link6 span {left:414px;width:73px;}
  17. #page7 ul#nav li.link7 span {left:487px;width:85px}

The HTML is still relatively lightweight and we just need to add the span after the anchor as follows.

  1. <ul id="nav">
  2. <li class="link1"><a href="menupage1.htm">Link 1</a><span></span></li>
  3. <li class="link2"><a href="menupage2.htm">Link 2</a><span></span></li>
  4. <li class="link3"><a href="menupage3.htm">Link 3</a><span></span></li>
  5. <li class="link4"><a href="menupage4.htm">Link 4</a><span></span></li>
  6. <li class="link5"><a href="menupage5.htm">Link 5</a><span></span></li>
  7. <li class="link6"><a href="menupage6.htm">Link 6</a><span></span></li>
  8. <li class="link7"><a href="menupage7.htm">Link 7</a><span></span></li>
  9. </ul>

This will place the span over the current link and stop it from being clicked. I have also added a cursor style so that the cursor isn’t a pointer when over the current item so that it doesn’t look like a link either.

The effect of this code is that the current item cannot be clicked and will show a default state if hovered. This is a good visual clue to users that they are on the current item. We now have a range of effects all happening on our menu.

All the menu items switch off on hover.

The currently hovered item highlights unless it is the current page link.

If it is the current page link it can’t be clicked.

The currently selected menu item is shown by default using the body ID.

A small pointer image is added to the current page menu item.

Refer to the Full Demo for the whole code and CSS as the CSS is in the head. You can navigate to each page and see how the body IDs are working to effect the change to the current page menu item.

Drawbacks

The drawbacks are that quite a large image is needed for a big menu such as this but even so the image I used was only about 35k and could have been optimized a lot better.

There is also an accessibility drawback with using images for navigation as a user may have CSS or Images switched off (I know that’s not really likely but I need to point it out all the same). We have catered for screen readers by adding some real text into the anchor which they can read. However, for normal users there would be a problem if images were disabled but CSS was still on. In this case there would be no text to see because we pushed it off-screen. If images and CSS were switched off then we would be ok because the default text will come back into view. As already mentioned above the most foolproof method is shown here but it would over-complicate this example to implement all that as well.

That about wraps it up for now and I’m sure you can find other clever uses on which to use these techniques. As I always say these articles are not really about the end result but more about the journey itself.

4 Responses to “Navigate-This”

1 Dan Schulz

Interesting, Paul.

2 Rob Lowe

hmmm…

Why don’t you use a sprite map instead of a full image matrix?, the image could easily be a 1/4 the size, it would take just a little bit more CSS, but that’s what we are good at, right?

I would be happy to provide an example.

3 Paul OB

Hi Rob,

Id’ be happy to see your example and any improvements you might like to make.

I just put these ideas out there and expect people to take them and improve on them or find different ways of doing the same things.

That’s the fun of CSS :)

However, I’m not quite sure you have grasped what’s going on in the above demo though because I can’t see an easier way to change all the other menu states without overlaying the whole row and without adding extra HTML and loads of separate images. You could nest a span and swap the image into the span while overlaying the whole row in the anchor but that really wouldn’t compete with what my version can offer.

Remember that my example allows all the other image states to change slightly at each stage also. The example I posted only shows the highlighted items with a glow but the graphic does bleed into the menus either side slightly which you can’t do with a single sprite image because it’s different for each menu.

This technique actually allows for a gradient, shadow, sunburst effect or glow to radiate out to the edges from each menu item selected and therefore the whole row could be different for every selection and would need to have a complete row image for every selection. There would be no room for slicing because each row is a separate unique image.

The image matrix would be the only way to do this.

Therefore I would be glad to see your example that competes on the same terms and not simply a single image swap as that wasn’t the point of the article. You must be able to effect a change to every other menu item and have the ability for the whole image to vary with each menu item.

Because that’s what my example can do :)

4 Disjointed CSS

[…] a simple effect but works most anywhere and can provide similar functions as shown in a previous article which used an image matrix for similar effects. I’m sure you can come up with more interesting […]

mulberry sale spyder womens jacket cheap new balance 574 mulberry outlet cheap new balance 574 arcteryx outlet mulberry sale spyder womens jacket mulberry sale spyder womens jacket mulberry outlet mulberry outlet new balance 574

Popular Articles

Top 10 Commentators


Subscribe to this feed! Subscribe by Email!

Random Bits Podcast

You need to download the Flash player from Adobe

Blogs Worth Reading