CSS and XHTML drop-down menu (vertical and horizontal)

How to create an drop-down menu that contains severals menus that each have sub.menus. This tutorial was updated in August 2004 to be simpler and to add functions especially concerning accessibility.

Note: This update was inspired for the Javascript part of the excellent ElMoustiko's tutorial (fr)... a young designer who will go far!
Also, read about this other approach (fr) signed Jep, very interesting and that will show you other techniques to create drop-down menus.

See tutorial's examples:

Why do I need to use Javascript?

A drop-down menu can very well be created only with CSS, without using any scripts languages, so why use Javascript in this tutorial?
There are two reasons for this. The first one is that the original function for CSS sheets is to take care of the page setting and not of the dynamic aspects of the document. The dynamics belongs to Javascript (or ECMAScript). The pseudo-class (:hover) is really in between these: it define a reaction when hovering, but it could also be used for more dynamic applications.

The second reason for this is that the pseudo-class (:hover) used correctly would enable you to create this type of drop-down menu easily and without using Javascript is unfortunately not really well understood by navigators such as Internet Explorer (With IE, this pseudo-class is not taken in consideration unless applies to the <a>) tag.

There is no choice but to use a Javascript function to display/mask the sub-menus and we will call this function by using "onmouseover" or "onclick" detectors.

We will also use the Definition Lists (<dl>, <dt> and <dd>) for our drop-down menu's structure. <dl> will contain the menu. <dt> will be the "title", that is "menu1", "menu2", ... and <dd> will define each sub-menu.

Definitions lists and menus?

A menu is a list of elements being displayed vertically or horizontally. Using lists (<ul> et <li>) is the best semantic choice to structure a simple menu.

Drop-down menus are a particular case since there is a hierarchy within menus and sub-menus. Lists(ul, li) on the other hand cannot express this hierarchy structurally and clearly (unless you want to place title tags within the menu).

Definitions lists can be used to structure this kind of menu since the W3C definition is quite unclear to allow this kind of use.

If there is a direct relation between elements, definitions lists can be used this way.

In this case, each menu element (dt) will be defined by its sub-menus (dd).

A few websites already have developed this structure: example of a translation on Pompage.net, example of the Microsoft menu revisited:

CSS will define the menus (dt) and sub-menus (dd and li list). It will also define that sub-menus should be hidden by default.

Vertical Drop-Down Menu

View result

Vertical drop-down menu will need three complimenting parts to work out:

  1. (X)HTML code to define the general structure (use of definitions lists for example)
  2. Javascript script to define behavior when menus are hovered onto
  3. CSS code to define format and elements positionning

Structure: HTML

As I explained in the introduction, the menu structure will be created with lists (ul/li) and definitions lists.

DT tags will define parent menus and DD tags will encompass sub-menus. There are four planned menus. Each one of the parent menu has sub-menus, except for the first one -this one won't have sub-menus but a direct link to another page. Everything can be configured according to your preferences or needs.

Behavior when hovered onto is defined as follows: when clicking (onclick) on a parent menu (dt) the sub-menu indicated in the "show" function will appear. If no sub-menu is defined there, then the behavior will be to shut down the sub--menus that are already showing.

HTML code:

<dl id="menu">

		<dt onclick="javascript:show();"><a href="#">Menu 1</a></dt>
			
		<dt onclick="javascript:show('smenu2');">Menu 2</dt>
			<dd id="smenu2">
				<ul>
					<li><a href="#">sub-menu 2.1</a></li>
					<li><a href="#">sub-menu 2.2</a></li>
					<li><a href="#">sub-menu 2.3</a></li>
				</ul>
			</dd>	

		<dt onclick="javascript:show('smenu3');">Menu 3</dt>
			<dd id="smenu3">
				<ul>
					<li><a href="#">sub-menu 3.1</a></li>
					<li><a href="#">sub-menu 3.1</a></li>
					<li><a href="#">sub-menu 3.1</a></li>
					<li><a href="#">sub-menu 3.1</a></li>
					<li><a href="#">sub-menu 3.1</a></li>
					<li><a href="#">sub-menu 3.1</a></li>
				</ul>
			</dd>

		<dt onclick="javascript:show('smenu4');">Menu 4</dt>
			<dd id="smenu4">
				<ul>
					<li><a href="#">sub-menu 4.1</a></li>
					<li><a href="#">sub-menu 4.1</a></li>
				</ul>
			</dd>
	
</dl>

Behavior: Javascript

A Javascript script will define behavior when clicking on parent menus.

When the function is called with "onclick", here is what happens: tos tart, all sub-menus close (display:none), then the sub-menu indicated in the "onclick" will appear (display:block).
If no sub-menu is specified, as in menu 1, only the first phase occurs. All displayed sub-menus close.

Window onload?

As you can see in the Javascript code below, the script calls the function "show()" when the page is charging. When calling ("show" empty), all sub-menus that were apparent go in hiding as soon as the document loads up.

It would have been easier to mask these sub-menus by defining their CSS with "display:none", which was the case in the first version of this tutorial. So why use Javascript to get the same effect?

The point is that it is more accessible that way, it is easier to operate: there is a lot of internet users' machines with disabled javascript.
For these users, the menu must stay accessible, which would not have been the case if the sub-menus had been hidden with CSS, because then they would stay hidden.
For our purpose, menus are hidden when the document loads up, but only if Javascript is enabled. In all other cases, the menu stays navigable even if no behavior to hovering is started.

Javascript code - to place in HEAD tags:

<script type="text/javascript">
<!--
window.onload=show;
function show(id) {
var d = document.getElementById(id);
	for (var i = 1; i<=10; i++) {
		if (document.getElementById('smenu'+i)) {document.getElementById('smenu'+i).style.display='none';}
	}
if (d) {d.style.display='block';}
}
//-->
</script>

Formating: CSS

CSS code will position the different elements, format them (color, background, border, and more) and mask sub-menus when the page will be charging by applying a "display:none" on these sub-menus (dd).

Here is the complete CSS code to place in HEAD tags or in a separate .css file without <style type="text/css"media-"screen" and </style> tags:

<style type="text/css" media="screen">
<!-- 
body {
margin: 0;
padding: 0;
background: white;
font: 80% verdana, arial, sans-serif;
}
dl, dt, dd, ul, li {
margin: 0;
padding: 0;
list-style-type: none;
}
#menu {
position: absolute; /* Menu position that can be changed at will */
top: 0;
left: 0;
}
#menu {
width: 15em;
}
#menu dt {
cursor: pointer;
margin: 2px 0;;
height: 20px;
line-height: 20px;
text-align: center;
font-weight: bold;
border: 1px solid gray;
background: #ccc;
}
#menu dd {
border: 1px solid gray;
}
#menu li {
text-align: center;
background: #fff;
}
#menu li a, #menu dt a {
color: #000;
text-decoration: none;
display: block;
border: 0 none;
height: 100%;
}
#menu li a:hover, #menu dt a:hover {
background: #eee;
}
-->
</style>

Now the vertical drop-down menu is working!

Horizontal Drop-Down Menu

Based on the same principle, here are the codes to create a horizontal drop-down menu. This time we will use "onmouseover" detector instead of "onclick".

Note for accessibility: As Chantal remarked: don't forget that all functions that are triggered by an event must be independent from the tool that was used. It is recommended to use onfocus and onblur as a complement to onmouseover and onmouseout for example.

Careful with alignment! Drop-down menus use the property "display:block" and "display:none". At first and when they are masked, sub-menus have the value "none", this means that they don't occupy any space on the page.
When they appear, they occupy a space that did not exist before, and they can "push" the rest of your site!
This is why in these cases, the menu and site should always be positioned separately, each in absolute position and they need to be given a separate z-index (depth) as you can see on the sample result. In this case the menu will be placed above the rest of the site and will appear without disturbing it.

View result

HTML code:

<div id="menu">
	<dl>
		<dt onmouseover="javascript:show();"><a href="" title="Return to home">Home</a></dt>
	</dl>
	
	<dl>			
		<dt onmouseover="javascript:show('smenu1');">Menu 1</dt>
			<dd id="smenu1">
				<ul>
					<li><a href="#">sub-menu 1.1</a></li>
					<li><a href="#">sub-menu 1.2</a></li>
					<li><a href="#">sub-menu 1.3</a></li>
					<li><a href="#">sub-menu 1.4</a></li>
					<li><a href="#">sub-menu 1.5</a></li>
					<li><a href="#">sub-menu 1.6</a></li>
				</ul>
			</dd>
	</dl>
	
	
	<dl>	
		<dt onmouseover="javascript:show();"><a href="">Menu 2</a></dt>
	</dl>
	
	<dl>	
		<dt onmouseover="javascript:show('smenu3');">Menu 3</dt>
			<dd id="smenu3">
				<ul>
					<li><a href="#">sub-menu 3.1</a></li>
					<li><a href="#">sub-menu 3.2</a></li>
					<li><a href="#">sub-menu 3.3</a></li>
					<li><a href="#">sub-menu 3.4</a></li>
					<li><a href="#">sub-menu 3.5</a></li>
				</ul>
			</dd>
	</dl>
	
	<dl>	
		<dt onmouseover="javascript:show('smenu4');">Menu 4</dt>
			<dd id="smenu4">
				<ul>
					<li><a href="#">sub-menu 4.1</a></li>
					<li><a href="#">sub-menu 4.2</a></li>
					<li><a href="#">sub-menu 4.3</a></li>
				</ul>
			</dd>
	</dl>
	
</div>

Javascript function is strictly the same than for vertical drop-down menu (see above).

CSS code:

<style type="text/css" media="screen">
<!-- 
body {
margin: 0;
padding: 0;
background: white;
font: 80% verdana, arial, sans-serif;
}
dl, dt, dd, ul, li {
margin: 0;
padding: 0;
list-style-type: none;
}
#menu {
position: absolute; /* Menu position that can be changed at will */
top: 0;
left: 0;
z-index:100;
width: 100%; /* precision for Opera */
}
#menu dl {
float: left;
width: 12em;
}
#menu dt {
cursor: pointer;
text-align: center;
font-weight: bold;
background: #ccc;
border: 1px solid gray;
margin: 1px;
}
#menu dd {
display: none;
border: 1px solid gray;
}
#menu li {
text-align: center;
background: #fff;
}
#menu li a, #menu dt a {
color: #000;
text-decoration: none;
display: block;
height: 100%;
border: 0 none;
}
#menu li a:hover, #menu li a:focus, #menu dt a:hover, #menu dt a:focus {
background: #eee;
}
#site {
position: absolute;
z-index: 1;
top : 70px;
left : 10px;
color: #000;
background-color: #ddd;
padding: 5px;
border: 1px solid gray; 
}
-->
</style>

Variation: disappearing sub-menus:

The above version lets sub-menus appear even when they are not hovered over.

If you'd rather have them disappear when the mouse is not positioned over them any longer, you only need to add the behavior: onmouseout="javascript:show()" on the sub-menus (dd) as in the following code:

<div id="menu">
	<dl>
		<dt onmouseover="javascript:show();"><a href="" title="Return to home">Home</a></dt>
	</dl>
	
	<dl>			
		<dt onmouseover="javascript:show('smenu1');">Menu 1</dt>
			<dd id="smenu1" onmouseover="javascript:show('smenu1');" onmouseout="javascript:show('');">
				<ul>
					<li><a href="#">sub-menu 1.1</a></li>
					<li><a href="#">sub-menu 1.2</a></li>
					<li><a href="#">sub-menu 1.3</a></li>
					<li><a href="#">sub-menu 1.4</a></li>
					<li><a href="#">sub-menu 1.5</a></li>
					<li><a href="#">sub-menu 1.6</a></li>
				</ul>
			</dd>
	</dl>
	
	
	<dl>	
		<dt onmouseover="javascript:show('smenu2');">Menu 2</dt>
			<dd id="smenu2" onmouseover="javascript:show('smenu2');" onmouseout="javascript:show('');">
				<ul>
					<li><a href="#">sub-menu 2.1</a></li>
					<li><a href="#">sub-menu 2.2</a></li>
					<li><a href="#">sub-menu 2.3</a></li>
					<li><a href="#">sub-menu 2.4</a></li>
				</ul>
			</dd>
	</dl>
	
	<dl>	
		<dt onmouseover="javascript:show('smenu3');">Menu 3</dt>
			<dd id="smenu3" onmouseover="javascript:show('smenu3');" onmouseout="javascript:show('');">
				<ul>
					<li><a href="#">sub-menu 3.1</a></li>
					<li><a href="#">sub-menu 3.2</a></li>
					<li><a href="#">sub-menu 3.3</a></li>
					<li><a href="#">sub-menu 3.4</a></li>
					<li><a href="#">sub-menu 3.5</a></li>
				</ul>
			</dd>
	</dl>
	
	<dl>	
		<dt onmouseover="javascript:show('smenu4');">Menu 4</dt>
			<dd id="smenu4" onmouseover="javascript:show('smenu4');" onmouseout="javascript:show('');">
				<ul>
					<li><a href="#">sub-menu 4.1</a></li>
					<li><a href="#">sub-menu 4.2</a></li>
					<li><a href="#">sub-menu 4.3</a></li>
				</ul>
			</dd>
	</dl>

View result

print article

Raphaël GOETTER
www.alsacreations.com