Improved Navigation Image Replacement

  • Published: 04.28.2007
  • Categories: Tutorial
  • Comments: 5

So my co-worker at work today was slicing a design, and the designer said that the navigation had to be images. Being the good little accessibility people that we were, we were trying to figure out a way to use images, but have text also show up when images are disabled.

So my co-worker went with the quick fix (for now), a JavaScript onmouseover solution, and I told him that it was lame. I told him there had to be a way to use CSS to do it. I didn't have time to do it at work, so I played with it, and I have found a solution.

The Markup

Ok, so everyone knows to mark-up navigation in an unordered list:

<ul id="nav">
 <
li><a href="#">Text to Cover Here</a></li>
 <
li><a href="#">Text to Cover Here</a></li>
 <
li><a href="#">Text to Cover Here</a></li>
 <
li><a href="#">Text to Cover Here</a></li>
</
ul

Now, instead of placing an image in the anchor tag and then using JavaScript to change the hover state, I thought, why not just add an extra span at the end of the anchor tag. I chose the end, but it also works with it at the beginning. I think it makes more sense to have it at the end though, so that the text within the anchor tag is read first by screen readers and bots. So this is what our markup looks like now:

<ul id="nav">
 <
li><a href="#">Text to Cover Here<span></span></a></li>
 <
li><a href="#">Text to Cover Here<span></span></a></li>
 <
li><a href="#">Text to Cover Here<span></span></a></li>
 <
li><a href="#">Text to Cover Here<span></span></a></li>
</
ul

Let’s Style It

So I started off with just some normal styles that you would apply to a navigation: zeroing out margins and paddings, floating it, removing the list-styling, and giving it a width.

Next, I floated the list items so that they would be in a line horizontally. This is where it finally gets interesting, I promise. I styled the anchor tag like so:

ul#nav li a {
 
background#FFFF99;
 
displayblock;
 
height30px;
 
padding0 5px;
 
width115px;

Since this is just an example, I put a random background color on (to make sure it didn’t show through in the final example), and I gave them all the same width. Not a likely situation, but I didn’t feel like giving each list item an id. That should be self explanatory enough.

Now, when I think back to the talk that Eric Meyer at An Event Apart Boston, he kept stressing that to a browser, an element is just an element, and you can do anything to it. So, my plan was to just set the anchor tag to be relatively positioned so that it would contain the span.

The next step is to add the background image to the span. Now one thing that needs to be realized is that you cannot use a transparent image. But I don’t think that really causes much of a problem in most cases.

ul#nav li a span {
 
backgroundurl(/images/content/2008/01/nav.gifno-repeat 0 -30px;
 
cursorpointer;
 
displayblock;
 
height30px;
 
left0;
 
positionabsolute;
 
top0;
 
width125px;

The only things to note from that is that I combined the normal and hover states into one awesome image (I wasn’t worried about how pretty it looked). I also had to add the cursor property for our best friend, IE6.

Ok, cool. We are on our way. Now we just need to shift the background image on the hover state:

ul#nav li a:hover span { background: url(/images/content/2008/01/nav.gif) no-repeat 0 0; } 

Voilà! It works like a charm. Check out the example.

But…

Did you check it in IE6? When I checked it in Firefox and IE7, everything worked beautifully. You can turn off the images and you get the text underneath.

When you do check it in IE6, you will notice that the hover states stay on. It’s very odd.

After a lot of tinkering, I finally found something that worked. If you add the following to the anchor when it is in its hovered state, it for some reason fixes it:

ul#nav li a:hover { background: 0 0; } 

I have no explanation, but it does not seem to have any adverse affects on other browsers.

Let’s Bulletproof It

We can add a simple property to the anchor tag so that when someone resizes their text, it does not poke out from under the image:

ul#nav li a {
 
background#FFFF99;
 
displayblock;
 
height30px;
 
overflowhidden/*Added for bulletproofing*/
 
padding0 5px;
 
positionrelative;
 
width115px;

Check out the final example.

More Bulletproofing

If you wanted the text to resize gracefully when images are disabled, I suppose you could set your height in ems. Then you would just need to build some extra blank space into your image, so that it would work when images are enabled.

So hopefully that helps out my co-worker. I think it potentially solves a pretty big problem with CSS image navigation with images disabled.

5 Comments

Adrian Turner

04.29.2007

You know what this helps me out… after i spent an hour and a half fixing it on my own today jeesh man pick up the phone and call a dude…

I’m reading this about and hour after fixing it myself, I had to go another route as my items were not equal in length.

Thanks for looking into it though, but I told you I was going to fix that problem this weekend.

By the way the reason that it took me so long is that I didn’t notice that my empty span was outside of my anchor tag.

Mark Hendricks

12.08.2008

The “Final” Link in this tutorial loads a different page.
Just thought i should let you know.  There’s an uppercase “R” in that link that shouldn’t be. 

Otherwise…nice work!!

http://trevordavis.net/play/imagereplacement/final/

Trevor

12.08.2008

@Mark Hendricks-
Whoa, weird. Thanks for letting me know.

Waleed Eissa

07.12.2009

I wrote almost the same code and have been struggling all day long to get it to work with IE6. Your fix works like a charm! thanks for sharing

iron77

01.17.2010

Thanks for the hover state stay on fix! One of the deeper hidden IE6 errors yet imo, congrats for finding it out :)
——-

Too late, comments are closed!

Don’t worry, you can email me or contact me on Twitter.