Creating a Drop-down Menu

We had an article on MSDN for creating a drop-down menu, but after repeated attempts, I, like many of our customers, couldn't make the code work, so we're in the process of removing it.  We will (mostly likely) eventually replace it.  In the meantime, though, I promised that I would provide an alternative.  I have working code for a drop-down menu that works in IE, Mozilla, and Netscape Navigator (sort of -- I've noticed some funky onmouseout behavior in Mozilla and Netscape Navigator that I haven't been able to resolve).

The example is comprised of HTML, JavaScript, and CSS and is contained in the following two files:

Here's a simple explanation of the menu table and the accompanying script.

Menu Table

The menu table is simple, but to make it to be usable on any ... uh, almost ... page, I surrounded the entire table with a DIV element.  The following code shows a shortened version of the menu table with the DIV element.  The DIV element has an id attribute value so that the JavaScript code can access the child elements.  This was necessary in order to allow anyone who might use this code to have <div> tags elsewhere on the page without interfering with the expand/collapse functions of the JavaScript.

 <div id="menu">
    <table width="100%">
       <tr>
          <td>
             ... (missing code)
          </td>
       </tr>
    </table>
 </div>

The table has a width of 100%, but if you wanted to specify an exact pixel measurement, you could easily do that without messing up the menus.  The table is comprised of one row (TR element) with multiple columns (TD elements).  In the live version, there are five columns with drop-down menus.  Each drop-down menu lives within its own cell in the table.  The code within this TD element is somewhat complex to provide for the proper expand/collapse funtionality of the drop-down menu.  The following shows the code for the first TD element.  Each of the TD elements are almost exactly the same with a few exceptions, which I'll explain in a moment.

 <td class="button" width="20%" valign="top" id="item1"
    onmouseover="expand(this.id, 'menu1');"
    onmouseout="collapse(this.id, 'menu1');">
       <p><b>Menu 1</b></p>
       ... (missing code)
 </td>

The class attribute names the style in the CSS that defines the table cells appearance when a user first displays the page.  The width attribute is set to 20% so that each of the five drop-down menus will be of equal space.  Again this is an easy thing to change if you want to modify the code to suit your own needs.  The id attribute is necessary as the scripts use these to access the TD element.

The missing code is the nested DIV element that contains the items in the drop-down menu.  The DIV element for the first menu item is shown below.

 <div id="menu1" class="exp" onmouseout="collapse('item1', this.id);">
    <p class="button"><a href="page.htm" class="menuitem">Item 1</a></p>
    <p class="button"><a href="page.htm" class="menuitem">Item 2</a></p>
    <p class="button"><a href="page.htm" class="menuitem">Item 3</a></p>
    <p class="button"><a href="page.htm" class="menuitem">Item 4</a></p>
 </div>

The nested DIV element also provides a value for the id attribute. Again, the class attribute specifies the style to use when displaying the menu. Within the DIV element are paragraphs (P elements), with a class attribute value of "button", a style defined in the attached CSS file, and links (A elements), with a class attribute value of "menuitem", another style defined in the attached CSS.  The menuitem style defines link, visited, hover, and active link styles.

Then, of course, there are the onmouseover and onmouseout events. The top-level menu item, contained in the TD element, specifies both the onmouseover and onmouseout events.  However, the drop-down menu, contained in the nested DIV element, specifies only the onmouseout event.  The JavaScript functions that these events call require both the id attribute for the TD element and the nested DIV element.

If you modify this script, adding or removing drop-down menus, you will want to change the second argument for these events. The following code shows the parts that you will need to change as you add and remove menus. The red code shows the id attribute for the top-level menu item (TD element) and all the places where you would need to change this in the event function calls, and the blue code shows the id attribute for the drop-down menu (DIV element) and all the places where you would need to change this in the event functions calls.  Basically, all the code in red must match and all the code in blue must match.

 <td class="button" width="20%" valign="top" id="item1"
    onmouseover="expand(this.id, 'menu1');"
    onmouseout="collapse(this.id, 'menu1');"><p><b>Menu 1</b></p>
    <div id="menu1" class="exp" onmouseout="collapse('item1', this.id);">
       <p class="button"><a href="page.htm" class="menuitem">Item 1</a></p>
       <p class="button"><a href="page.htm" class="menuitem">Item 2</a></p>
       <p class="button"><a href="page.htm" class="menuitem">Item 3</a></p>
       <p class="button"><a href="page.htm" class="menuitem">Item 4</a></p>
    </div> 
</td>

Notes

  • Because the event function calls use this.id, there's no need to specify the id attribute values for these.
  • The code is purple above is code that you can safely change without affecting how the menus function.

If you look at the full code in the page (see Drop-down menu example above), the only difference between each of the TD/DIV element pairs is the id attribute value.  You'll notice that each menu is numbered consecutively (i.e., item1/menu1, item2/menu2, item3/menu3, etc.).  I did this for ease of use.  You can, of course, provide any id attribute values you want, as long as they're not the same as another element on the page and as long as you change the appropriate arguments in the event function calls so that they match as shown above.

JavaScript Functions

Now for the functions. There are three functions: one for collapsing all drop-down menus, one for expanding a single menu, and one for collapsing a single menu.

The first one, called collapseAll, collapses all menus. The collapseAll function takes no arguments and is called once when the page loads in the onload event for the BODY. This code is relatively simple and is shown in the following example:

 function collapseAll()
 {
    var menuDiv = document.getElementById("menu");
    var divs = menuDiv.getElementsByTagName("div");
    var div;
    for (i = 0; i < divs.length; i++)
    {
       div = divs[i];
       div.style.visibility = "hidden";
       div.style.display = "none";
    }
 }

The second function, called expand, is where most of the action happens ... uh, at least half of it.  The expand function has two parameters: Then, the function is basically a bunch of scripted CSS that is used to show the DIV element and change the appearance of the top-level menu.  The expand function is shown below:

 function expand(s, m)
 {
    var browser = window.navigator.appName;
    var dif = 0;
    if (browser != "Microsoft Internet Explorer") { dif = 12; }
    var d = document.getElementById(m);
    var td = document.getElementById(s);
    var left = td.offsetLeft;
    var top = td.offsetTop + 58;
    var width = td.offsetWidth - dif;
    td.style.color = "#FFFFFF";
    td.style.backgroundColor = "#686496";
    d.style.top = top;
    d.style.left = left;
    d.style.width = width;
    d.style.position = "absolute";
    d.style.visibility = "visible";
    d.style.display = "block";
 }
 

There are a few things you should know about this code.

  • First, there is a difference between how IE displays the menu and how Mozilla and Netscape Navigator display the menu. (Note that I haven't tested this code in Opera.)  In Moz and NN, the width doesn't show up properly, so I had to specify a value that would account for the difference between the offsetWidth and the actual width of the table in these browsers.  The number isn't arbitrary; I determined it by trial and error, and this was the only number than worked.  I have no idea why this is different only that it is and this number fixes it.  If you make huge changes to the menu, this number may need to be changed also.
  • Second, the number added to the offsetTop value accounts for the height of the first line in the top-level menu as well as the top heading paragraph.  Again, this number is not arbitrary and accounts for the space between the top of the browser and the top of the drop-down menu. I arrived at this number by a trial and error process.  If your top-level menu text runs onto multiple lines and/or you have a large graphic heading or other things above the menu bar, this number will likely change.
  • Third, if you want to change the appearance of the menu, you can change any of the values in purple in the code above to appropriate and desired values without affecting how the script functions.

And finally, the third function, called collapse, collapses a single drop-down menu.  This is where the other half of the action happens.  The collapse function has two parameters: the first is the id attribute value for the TD element (the top-level menu item), the second is is the id attribute value for the nested DIV element (the drop-down menu). The collapse function is shown in the following code:

 function collapse(s, m)
 {
    var d = document.getElementById(m);
    var td = document.getElementById(s);
    td.style.color = "black";
    td.style.backgroundColor = "#EEEEEE";
    d.style.position = "static";
    d.style.visibility = "hidden";
    d.style.display = "none";
 }

There's absolutely nothing major about this code.  It changes the appearance of the TD element back to the original formatting, and hides the DIV element.  Ideally, this formatting would match what is in the attached CSS, which in this case, it does. You can change any of the values in purple in the code above to appropriate and desired values without affecting how the script functions.

Conclusion

And that's all there is to it.  Have fun using this, and if you need help, please let me know. If you are looking for a drop-down menu, feel free to use it, or if you do an Internet search, you will find many more examples and just as many ways to create drop-down menus, and many of them much cooler than my litte example here.

Have fun, and enjoy playing with the code, and if you can tell me what's up with the Mozilla/Netscape issue, please do.  I've tried all types of arrangements for the code to see if order made a difference, if putting the event function call in a different place made a difference, and I've been relatively unsuccessful getting it to be completely functional in these browsers.  Sometimes the onmouseout event just doesn't fire.

Anyway, as usual, my only real caveat is that the code won't work at all if the user has scripting turned off in their browser, but IMHO, that's not a reason to not use it or any other script.