Using the DOM to create clean “graphic” Menus

This article was first published on (01-01-2006).

Years ago I asked if we needed table markup to contain images used in menus; today I'm wondering if we even need markup for those images 🙂

This solution is about replacing this type of markup:

<table border="0" cellpadding="0" cellspacing="0" width="300">
<!-- fwtable fwsrc="Untitled" fwbase="image.jpg" fwstyle="Dreamweaver" fwdocid= "742308039" fwnested="0" -->
        <td><a class="current" href="#" onMouseOver="MM_swapImgage()" onMouseOut="MM_swapImgRestore()"><img name="image_r1_c1" src="image_r1_c1.jpg" width="63" height="65" border="0" alt="Home"></a></td>
        <td><a href="#" onMouseOver="MM_swapImgage()" onMouseOut="MM_swapImgRestore()"><img name="image_r1_c2" src="image_r1_c2.jpg" width="78" height="65" border="0" alt="Services"></a></td>
        <td><a href="#" onMouseOver="MM_swapImgage()" onMouseOut="MM_swapImgRestore()"><img name="image_r1_c3" src="image_r1_c3.jpg" width="56" height="65" border="0" alt="Work"></a></td>
        <td><a href="#" onMouseOver="MM_swapImgage()" onMouseOut="MM_swapImgRestore()"><img name="image_r1_c4" src="image_r1_c4.jpg" width="93" height="65" border="0" alt="Standards"></a></td>
        <td><a href="#" onMouseOver="MM_swapImgage()" onMouseOut="MM_swapImgRestore()"><img name="image_r1_c5" src="image_r1_c5.jpg" width="73" height="65" border="0" alt="Contact">Standards</a></td>

With this:

<ul id="TJK_tipMenu">
    <li><a class="ubiAt" href="#" id="Home_63">Home</a></li>
    <li><a href="#" id="Services_78">Services</a></li>
    <li><a href="#" id="Work_56">Work</a></li>
    <li><a href="#" id="Standards_93">Standards</a></li>
    <li><a href="#" id="Contact_73">Contact</a></li>

See this script in action

Because a menu is a list of links, we are using an Unordered List (ul) (anything else than a List would not be semantically correct).

As you can see, we are actually not using img elements but real text inside the anchors (a plus for accessibility).

Note that the id value of the UL is important because it is used by the script as a hook.

The class "ubiAt" is a "marker" used to style the current link the same way as the rollovers.

How it works

CSS rules take care of the styling of the UL; then, when the page loads, the script automatically inserts the images, creates rollovers, and "marks" the current link (depending on which anchor " ubiAt" is used with).

The magic relies on the id value of the A elements (hence it is extremely important to respect the naming convention). The script finds the images by doing some "string manipulation". For example, this is the deduction done by the script for the first link whose id is: Home_63:

  1. The file name for this image is "Home.gif" (the word at the left of the underscore).
  2. The width of that image is "63px" (the value at the right of the underscore).
  3. The image to use for rollover is "Home-O.gif" (the original file name + "-O").

Note that the file extension is set in the script itself (it defaults to "gif"). If I did not mention the height of these images yet, it is because we'll pass that value to the script using a parameter.

How to make it work

The script

First, download (2 KB) and unzip the file into a directory within your site. Then, use a SCRIPT element to link the JS file (TJK_tipMenu.js) to your pages within the HEAD element of your documents, as shown below:

<script type="text/javascript" src="/js/TJK_tipMenu.js"></script>

Note: In this case, the file has been saved inside a directory called "JS" within the root folder.

The Stylesheet

Cut and paste the CSS rules below at the bottom of your stylesheet.

/* TJK_tipMenu v1.1.2 :: Firefox Mac bug fix       */
/* Copyright (c) 2006 TJKDesign - Thierry Koblentz */
/* EDITING IS REQUIRED HERE ************************/
/* values must be equal to the width of the images */
#Home_63 {width: 63px;}
#Services_78 {width: 78px;}
#Work_56 {width: 56px;}
#Standards_93 {width: 93px;}
#Contact_73 {width: 73px;}
/* this is to shrink-wrap the UL around the images */
/* height of the images, total width of the images */
ul#TJK_tipMenu {
    height: 65px;
    line-height: 65px;
    width: 363px;
/* EDITING IS OPTIONAL HERE ************************/
/* Make these links pretty in case Images are OFF  */
#TJK_tipMenu a {
    font-family: Arial,Helvetica,sans-serif;
    font-weight: bold;
#TJK_tipMenu a,
a:visited {
    background: #00adef;
    color: #000;
#TJK_tipMenu a:hover,
#TJK_tipMenu a:focus,
#TJK_tipMenu a:active {color: #ccc;}
#TJK_tipMenu * {
    border: 0 !important;
    padding: 0 !important;
#TJK_tipMenu li {float:left}
/* do not use "display:inline" here (it triggers a bug in Firefox Mac) */
#TJK_tipMenu a {
    text-align: center;
    float: left;
    display: block;
    white-space: nowrap;

These rules have two purposes:

Note that the most important declaration for you to edit in there is the height value for the ul (#TJK_tipMenu). This is because we use " float:left" for all anchors in the menu. Giving our list a height that equals the height of the inner floats helps us keep the latter within the boundaries of the ul. Floating that element would have the same result, but then we woud have to clear something while here there should be no need to.

The Markup

Now that we have taken care of the behavior and presentational layers (the script and CSS), it is time to work on the markup.

Follow these simple steps, keeping in mind that we assume the following:

  1. Create your menu using an UL with "TJK_tipMenu" for ID value.
  2. For each anchor in the list, use IDs following the proper format. Once again, ID values must contain an underscore; they should look like this: image name + underscore + width
  3. If you wish, you can use the ubiAT class on one of these anchors as a marker (optional).
  4. In the script, find the variable named "TJK_tipMenu_Folder_Path" and replace its value with the path to your image folder.
  5. If you're using images other than GIFs, look in the script for the variable "TJK_tipMenu_ext" and change its value to fit your needs.
  6. Now the last step is to call the function passing the value of the image height. If you do not have a favorite technique to do this, then go with the onload event of the body element, as in the example below:
    <body onload="TJK_tipMenu(65);">

In the above example, we're telling the script to use 65px as value for the height of the ul and images. You'll have to replace "65" with the height of your own images.

This is it! You've replaced a non semantic table full of images and all sort of attributes with a clean list of text links.