Ceci est une copie pour sauvegarde de l'article Un menu déroulant en CSS et XHTML (horizontal et vertical) tel que publié le 27 février 2005 par Raphaël GOETTER sur Alsacréations dans la rubrique Construction de menus en CSS sous licence Creative Commons 2.0 Paternité France .

Un menu déroulant en CSS et XHTML (horizontal et vertical)

Nous allons voir comment créer un menu déroulant comportant plusieurs menus, contenant chacun des sous-menus. Cette version de ce tutoriel a été remise à jour en août 2004 pour y apporter des simplifications et des fonctionnalités supplémentaires notamment au niveau de l'Accessibilité.

Note : Je vous donne également le lien vers une autre approche (fr) de Jep, très intéressante et qui vous proposera d'autres techniques pour créer un menu déroulant.
A suivre également les menus déroulants multi-niveaux de DoSimple (fr), propres, sémantiques et avec un code JavaScript complètement externalisé.

Voir les exemples du tutoriel :

Pourquoi utiliser du javascript ?

Un menu déroulant peut très bien se réaliser intégralement en CSS, sans utilisation de langages de scripts, alors pourquoi allons-nous utiliser javascript dans ce didactitiel?
Il y'a deux raisons à cela. La première est que la fonction originelle des feuilles de styles CSS est de s'occuper de la mise en page et non des aspects dynamiques de celle-ci. Ces derniers sont du domaine de Javascript (ou ECMAScript). Il s'avère que la pseudo-classe (:hover) est un peu située entre les deux : elle indique le comportement au survol mais pourrait très bien servir à des applications plus dynamiques.

La seconde raison est plus pragmatique : la pseudo-classe (:hover) qui, appliquée à bon escient, permettrait de réaliser ce genre de menus déroulants facilement et sans l'usage de javascript n'est malheureusement pas bien comprise par Internet Explorer (sur IE, cette pseudo classe n'est prise en compte que lorsqu'elle s'applique à la balise <a>).

Nous allons donc devoir utiliser une fonction javascript pour afficher / masquer nos sous-menus et nous appelerons cette fonction à l'aide des détecteurs "onmouseover" ou "onclick" selon les envies.

Nous utiliserons également les Listes de Définition (balises <dl>, <dt> et <dd>) pour structurer notre menu déroulant. La balise <dl> englobera le menu. Le <dt> sera le "titre", c'est à dire "menu1", "menu2", ... et les <dd> désigneront chaque sous-menu.

Listes de définitions et menu ?

Un menu est une liste d'éléments affichés verticalement ou horizontalement. L'utilisation de listes (balises <ul> et <li>) est donc le meilleur choix sémantique possible pour structurer un menu simple.

Les menus déroulants sont un cas un peu particulier dans la mesure où il y'a une notion de hiérarchie (menu > sous-menus). Or les listes (ul, li), ne peuvent pas exprimer structurellement et clairement cette hiérarchie (à moins de placer des balises de titres comme <Hn> au sein du menu).

Les listes de définitions peuvent être une bonne utilisation pour structurer ce genre de menu car la définition du W3C est assez large et vague pour permettre ce genre d'utilisation.

Il suggère que les listes de définitions peuvent servir d'avantage d'usages, du moment qu'il existe une relation directe entre les éléments.

Dans notre cas, chaque élément de menu (dt) sera décrit par les sous-menus (dd) qui le composent

Certains sites ont d'ailleurs développé assez loin cette structuration : exemple d'une traduction sur Pompage.net, exemple du menu Microsoft revisité .

Le CSS se contentera de définir nos menus (dt) et sous-menus (dd + liste li). Il précisera aussi que les sous-menus doivent être cachés par défaut.

Le menu déroulant vertical

Voir le résultat

Le menu déroulant vertical va nécessiter trois parties complémentaires pour fonctionner :

  1. Le code (X)HTML qui va définir la structure générale (utilisation des listes de définitions par exemple)
  2. Le script Javascript qui va indiquer les comportements au survol des menus
  3. Le code CSS qui va s'occuper de la mise en forme et des positionnements des éléments

La structure : le HTML

Comme expliqué dans l'introduction, la structure du menu sera réalisée à l'aide de listes (ul/li) et de listes de définition.

Les balises DT indiqueront les menus parents et les balises DD engloberont les sous-menus. Nous voyons qu'il y'a 4 menu prévus. Chaque menu parent possède des sous-menus, sauf le premier (il n'aura pas de sous-menus mais un lien direct vers une page). Mais tout est configurable selon vos souhaits, naturellement.

Le comportement au survol est prévu ainsi : en cliquant (onclick) sur un menu parent (dt), vous allez afficher le sous-menu indiqué dans la fonction "montre". Si aucun sous-menu n'est indiqué (cas du premier menu), alors le comportement sera simplement de fermer les sous-menus actuellement ouverts.

Code HTML :

<dl id="menu">

		<dt onclick="javascript:montre();"><a href="#">Menu 1</a></dt>
			
		<dt onclick="javascript:montre('smenu2');">Menu 2</dt>

			<dd id="smenu2">
				<ul>
					<li><a href="#">Sous-Menu 2.1</a></li>
					<li><a href="#">Sous-Menu 2.2</a></li>

					<li><a href="#">Sous-Menu 2.3</a></li>
				</ul>
			</dd>	

		<dt onclick="javascript:montre('smenu3');">Menu 3</dt>

			<dd id="smenu3">
				<ul>
					<li><a href="#">Sous-Menu 3.1</a></li>
					<li><a href="#">Sous-Menu 3.1</a></li>

					<li><a href="#">Sous-Menu 3.1</a></li>
					<li><a href="#">Sous-Menu 3.1</a></li>
					<li><a href="#">Sous-Menu 3.1</a></li>

					<li><a href="#">Sous-Menu 3.1</a></li>
				</ul>
			</dd>

		<dt onclick="javascript:montre('smenu4');">Menu 4</dt>

			<dd id="smenu4">
				<ul>
					<li><a href="#">Sous-Menu 4.1</a></li>
					<li><a href="#">Sous-Menu 4.1</a></li>

				</ul>
			</dd>
	
</dl>

Le comportement : le Javascript

Le script Javascript va s'occuper du comportement lors du clic sur les menus parents.

Lorsque la fonction est appelée à l'aide du "onclick", voici le déroulement : pour commencer, tous les sous-menus se ferment (display: none), puis, le sous-menu indiqué dans le "onclick" s'affiche (display: block).
Si aucun sous-menu n'est spécifié (cas du menu 1), seule la première phase a lieu : tous les sous-menus affichés se ferment.

Window onload ?

Comme vous le remarquez dans le code Javascript ci-dessous, le script appelle la fonction "montre()" au chargement de la page. Cet appel ("montre" vide) a pour effet de cacher tous les sous-menus dès le chargement du document.

Il aurait été plus simple de masquer ces sous-menus en définissant simplement leur CSS à "display:none" (c'était d'ailleurs le cas dans la première version du tutoriel), alors pourquoi avoir préféré utiliser un appel javascript pour obtenir le même effet ?

L'intérêt est une question d'Accessibilité, ou plutôt d'interopérabilité : il existe une part non négligeable d'internautes pour qui Javascript est désactivé.
Pour ces utilisateurs, le menu doit rester utillisable, ce qui n'aurait pas été le cas si les sous-menus avaient été cachés par CSS, car ils le resteraient.
Dans notre cas, les menus sont effacés au chargement, mais uniquement si javascript est actif. Dans les autres cas, le menu reste navigable même si aucun comportement au survol ne sera déclenché.

Code Javascript (à placer dans les balises HEAD) :

<script type="text/javascript">
<!--
window.onload=montre;
function montre(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>

La mise en forme : le CSS

Le code CSS va positionner les différents éléments, les mettre en forme (couleur, fond, bordures, etc.) et masquer les sous-menus au chargement de la page en appliquant un "display: none" sur ces sous-menus (dd).

Voici le code CSS complet (à placer dans les balises HEAD, ou dans un fichier .css séparé en ôtant les balises <style type="text/css" media="screen"> et </style>) :

<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; /* placement du menu, à modifier selon vos besoins */
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>

Notre menu déroulant vertical est fonctionnel !

Le Menu déroulant horizontal

Voici à présent, sur le même principe, les codes pour réaliser un menu horizontal déroulant (utilisation cette fois du détecteur "onmouseover" et non de "onclick")...

Note pour l'Accessibilité : Une remarque soulevée par Chantal : N’oubliez pas non plus que toutes les fonctionnalités activées par un événement doivent être indépendant de l’outil utilisé. En effet, il est recommandé d’utiliser onfocus et onblur en complément de onmouseover et onmouseout par exemple.

Attention aux décalages ! Les menus déroulants utilisent la propriété "display : block" et "display : none". Au départ et lorsqu'ils sont masqués, les sous-menus ont la valeur "none", cela signifie qu'ils n'occupent aucun espace dans la page.
En s'affichant, ils occupent alors un espace qui n'existait pas avant et peuvent "pousser" le reste de votre site !
C'est pourquoi, dans ces cas là, il faudra toujours positionner le menu et le site à part (chacun en position absolue) et leur donner un z-index (profondeur) différent, comme vous le voyez sur l'exemple de résultat. Dans ce cas, le menu sera placé au-dessus du reste du site et s'affichera sans le gêner.

Voir le résultat

Code HTML :

<div id="menu">

	<dl>
		<dt onmouseover="javascript:montre();"><a href="" title="Retour à l'accueil">Accueil</a></dt>

	</dl>
	
	<dl>			
		<dt onmouseover="javascript:montre('smenu1');">Menu 1</dt>

			<dd id="smenu1">
				<ul>
					<li><a href="#">Sous-Menu 1.1</a></li>

					<li><a href="#">Sous-Menu 1.2</a></li>

					<li><a href="#">Sous-Menu 1.3</a></li>
					<li><a href="#">Sous-Menu 1.4</a></li>

					<li><a href="#">Sous-Menu 1.5</a></li>

					<li><a href="#">Sous-Menu 1.6</a></li>
				</ul>

			</dd>
	</dl>

	
	
	<dl>	
		<dt onmouseover="javascript:montre();"><a href="">Menu 2</a></dt>
	</dl>

	
	<dl>	
		<dt onmouseover="javascript:montre('smenu3');">Menu 3</dt>

			<dd id="smenu3">
				<ul>
					<li><a href="#">Sous-Menu 3.1</a></li>

					<li><a href="#">Sous-Menu 3.2</a></li>

					<li><a href="#">Sous-Menu 3.3</a></li>
					<li><a href="#">Sous-Menu 3.4</a></li>

					<li><a href="#">Sous-Menu 3.5</a></li>

				</ul>
			</dd>
	</dl>
	
	<dl>	
		<dt onmouseover="javascript:montre('smenu4');">Menu 4</dt>

			<dd id="smenu4">
				<ul>
					<li><a href="#">Sous-Menu 4.1</a></li>
					<li><a href="#">Sous-Menu 4.2</a></li>

					<li><a href="#">Sous-Menu 4.3</a></li>
				</ul>
			</dd>
	</dl>

	

</div>

La fonction javascript est strictement la même que pour le menu vertical (cf plus haut)

Code CSS :

<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; /* placement du menu, à modifier selon vos besoins */
top: 0;
left: 0;
z-index:100;
width: 100%; /* correction pour 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>

Variante : les sous-menus disparaissent

La version proposée ci-dessus laisse apparaître les sous-menus même lorsqu'ils ne sont plus survolés.

Si vous préférez qu'ils se masquent lorsquela souris les quitte, il suffit d'ajouter le comportement onmouseout= "javascript:montre();" sur les sous-menus (dd) comme le montre le code suivant :

<div id="menu">
	<dl>

		<dt onmouseover="javascript:montre();"><a href="" title="Retour à l'accueil">Accueil</a></dt>
	</dl>
	
	<dl>			
		<dt onmouseover="javascript:montre('smenu1');">Menu 1</dt>

		<dd id="smenu1" onmouseover="javascript:montre('smenu1');" onmouseout="javascript:montre('');">
			<ul>
				<li><a href="#">Sous-Menu 1.1</a></li>
				<li><a href="#">Sous-Menu 1.2</a></li>

				<li><a href="#">Sous-Menu 1.3</a></li>
				<li><a href="#">Sous-Menu 1.4</a></li>
				<li><a href="#">Sous-Menu 1.5</a></li>

				<li><a href="#">Sous-Menu 1.6</a></li>
				</ul>
		</dd>
	</dl>
	
	
	<dl>	
		<dt onmouseover="javascript:montre('smenu2');">Menu 2</dt>

			<dd id="smenu2" onmouseover="javascript:montre('smenu2');" onmouseout="javascript:montre('');">
			<ul>
				<li><a href="#">Sous-Menu 2.1</a></li>
				<li><a href="#">Sous-Menu 2.2</a></li>

				<li><a href="#">Sous-Menu 2.3</a></li>
				<li><a href="#">Sous-Menu 2.4</a></li>
			</ul>

			</dd>
	</dl>
	
	<dl>	
		<dt onmouseover="javascript:montre('smenu3');">Menu 3</dt>
			<dd id="smenu3" onmouseover="javascript:montre('smenu3');" onmouseout="javascript:montre('');">

			<ul>

				<li><a href="#">Sous-Menu 3.1</a></li>
				<li><a href="#">Sous-Menu 3.2</a></li>

				<li><a href="#">Sous-Menu 3.3</a></li>
				<li><a href="#">Sous-Menu 3.4</a></li>
				<li><a href="#">Sous-Menu 3.5</a></li>

			</ul>

			</dd>
	</dl>
	
	<dl>	
		<dt onmouseover="javascript:montre('smenu4');">Menu 4</dt>

			<dd id="smenu4" onmouseover="javascript:montre('smenu4');" onmouseout="javascript:montre('');">
			<ul>

				<li><a href="#">Sous-Menu 4.1</a></li>
				<li><a href="#">Sous-Menu 4.2</a></li>

				<li><a href="#">Sous-Menu 4.3</a></li>

			</ul>
			</dd>
	</dl>

Voir le résultat