Un menu déroulant en CSS et XHTML (horizontal et vertical)
Le 27-02-2005 par Raphael dans Construction de menus en CSS.
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é.
Préambule : ce tutoriel est estampillé "délicat"; Il ne doit pas être considéré comme une référence en la matière car il comporte certains problèmes intrinsèques.
Pour résumer, le menu réalisé à l'aide de ce tutoriel vieillissant ne sera pas exempt de défauts, ni forcément accessible à tous.
Malgré ces problèmes, la raison d'être de ce tutoriel est de pallier à des techniques encore plus problématiques proposées un peu partout sur le Web et qui ne tiennent aucun compte de l'accessibilité ou de l'ergonomie du resultat.
Sachez que ce tutoriel, très demandé, est actuellement en complète refonte. Nous vous conseillons de patienter un peu avant de vous jeter sur les menus déroulants actuellement proposés... Ou d'opter pour le Menu en Accordéon avec JQuery.
Note : 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.
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
Le menu déroulant vertical va nécessiter trois parties complémentaires pour fonctionner :
- Le code (X)HTML qui va définir la structure générale (utilisation des listes de définitions par exemple)
- Le script Javascript qui va indiquer les comportements au survol des menus
- 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 !
Plusieurs niveaux de sous-menus ?
Si vous tenez à créer plusieurs niveaux de sous-menus, il faut savoir que ce n'est pas possible avec cette méthode.
Tout simplement parce que les sous-menus sont structurés par des éléments <a>. Si on veut rajouter des sous-sous-menus, il faudrait imbriquer de nouveaux éléments <a> dans ceux existants. Or ceci est interdit : <a> ne peut pas contenir d'autres <a>.
Bref, il faut se tourner vers les autres solutions dans les liens donnés au début du tutoriel. Celles ci procèdent différemment, avec une surcouche javascript.
Compatibilité Netscape 6
L'attribut onmouseover de la balise <dt> n'est pas pris en charge par le navigateur Netscape Navigator 6.0
pour une total compatibilté avec Netscape vous devrez appliquer onmouseover également sur le lien <a>
<dt onmouseover="javascript:montre('smenu1');">
<a onmouseover="javascript:montre('smenu1');"
href="">Accueil</a>
</dt>
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.
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>
Mise à jour : meilleure prise en compte de JavaScript
Les sous-menus, cachés dans le menu dynamique, sont montrés tous ouverts si javascript est désactivé. Le problème est qu'ils recouvrent du contenu.
Pour pallier à cet inconvénient, Chmel a prévu une variante de ce menu en gérant une nouvelle feuille comprenant le style du menu sans javascript est créée.
Un problème, une question ?
Vous avez des soucis avec ce tutoriel ? Venez en discuter dans le salon Spécifique aux Tutoriels et articles du Forum Alsacréations.
Aller plus loin :
Grâce au livre de Raphaël Goetter, apprivoisez les styles CSS et le langage (x)HTML, tout en plongeant dans la création de sites conformes aux standards web et accessibles à tous.




