Des menus déroulants grâce aux CSS

Marc Hertzog

Objectif : réaliser un menu déroulant XHTML + CSS (sans javascript) avec fonds transparents.

Inconvénients : seuls quelques navigateurs récents (Mozilla/Firebird, Safari) seront capables de rendre ce menu correctement. En contre-partie, comme IE Windows n'affichera pas les sous-menu, il y a possibilité d'utiliser des PNG avec transparence alpha.

En contre-partie, comme IE Windows n'affichera pas les sous-menu, il y a possibilité d'utiliser des PNG avec transparence alpha.

Attention : le menu déroulant décortiqué dans ce tuto ne fonctionnera pas dans Internet Explorer 6 et versions antérieures car ces navigateurs ne supportent pas la pseudo-classe :hover sur un autre élément que <a>. Ce navigateur ne supporte pas non plus les images au format PNG avec transparence alpha. Il existe des solutions, je vous laisse creuser.

- Titre : Des menus déroulants grâce aux CSS

- Auteur : Marc Hertzog

- Parution : 1 juin 2004

- Mise à jour : 24 avril 2009

- Licence : Copyright ® 2004 Marc Hertzog. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- La structure

- Le style

- Le Menu Magique

- Pour éviter un décalage du contenu lorsque le menu est déroulé

- Le résultat

La structure

La première étape consiste à définir la structure de notre menu. Dans mon cas, je vais faire simple pour ce premier essai et faire un seul sous niveau. On aura donc le premier niveau visible, et un sous-niveau déroulant.

Je vais commencer par le menu principal avec une liste non-ordonnée qui commence par un élément <ul>. Chaque item de cette liste de premier niveau démarre par un élément <li> et contient des liens <a> vers les rubriques de tout premier niveau. Ces liens sont importants pour les navigateurs qui n'afficheront pas les menus-déroulants :

<ul>
<li><a href="#">Partie 1</a></li>
<li><a href="#">Partie 2 </a></li>
<li><a href="#">Partie 3</a></li>
<li><a href="#">Partie 4</a></li>
</ul>

J'enchaîne en imbriquant des sous-niveaux. Dans la structure du code, ça va se traduire par une autre liste dans chaque item <li> de ma première liste :

<ul>
<li>
<a href="#">Partie 1</a>
<ul>
<li><a href="#">castor</a></li>
<li><a href="#">aligator</a></li>
<li><a href="#">musclor</a></li>
</ul>
</li>
<li>
<a href="#">Partie 2</a>
<ul>
<li><a href="#">whisky</a></li>
<li><a href="#">vodka</a></li>
<li><a href="#">gin</a></li>
<li><a href="#">vin</a></li>
<li><a href="#">champagne</a></li>
</ul>
</li>
<li>
<a href="#">Partie 3</a>
<ul>
<li><a href="#">pommes</a></li>
<li><a href="#">poires</a></li>
<li><a href="#">ananas</a></li>
<li><a href="#">pamplemousse</a></li>
<li><a href="#">banane</a></li>
<li><a href="#">raisins</a></li>
<li><a href="#">framboises</a></li>
<li><a href="#">fraises</a></li>
<li><a href="#">mirabelles</a></li>
<li><a href="#">groseilles</a></li>
</ul>
</li>
<li>
<a href="#">Partie 4</a>
<ul>
<li><a href="#">tomates</a></li>
<li><a href="#">haricots</a></li>
<li><a href="#">carrottes</a></li>
<li><a href="#">choux</a></li>
<li><a href="#">concombres</a></li>
<li><a href="#">courgettes</a></li>
<li><a href="#">endives</a></li>
<li><a href="#">navets</a></li>
<li><a href="#">epinards</a></li>
<li><a href="#">avocat</a></li>
</ul>
</li>
</ul>

Avant de commencer à mettre du style, je vais donner des identifiants et des classes à mes éléments pour pouvoir y accéder sans ambiguïté via ma feuille de style CSS. Comme mon menu de navigation va être unique sur ma page, je peux me permettre de donner un identifiant id unique à l'élément <ul> de premier niveau. Par contre je vais styler tous mes sous menu de la même façon. Je vais donc devoir leur affecter une même classe, pas d'id car ce dernier doit-être unique. Si je ne différencie pas mes sous-niveaux avec des classes, ils vont hériter de certaines propriétés de leur(s) élément(s) parent(s) dans l'arbre du document. Au final, on a le code suivant :

<ul id="menuDeroulant">
<li>
<a href="#">Partie 1</a>
<ul class="sousMenu">
<li><a href="#">castor</a></li>
<li><a href="#">aligator</a></li>
<li><a href="#">musclor</a></li>
</ul>
</li>
<li>
<a href="#">Partie 2</a>
<ul class="sousMenu">
<li><a href="#">whisky</a></li>
<li><a href="#">vodka</a></li>
<li><a href="#">gin</a></li>
<li><a href="#">vin</a></li>
<li><a href="#">champagne</a></li>
</ul>
</li>
[...]
</ul>

Le style

A partir de maintenant, tout se passe dans la feuille de style.

Pour cet exemple je vais définir la taille des caractères directement sur body histoire de ne pas alourdir le reste du code. Je vais définir la taille de mon texte en pixels (oui, je sais qu'il y a mieux comme unité). Pour bien voir la future transparence de mes sous-menus, je vais mettre une image de fond sur la page. Finalement, pour que ma page colle aux bords de la fenêtre du navigateur, je donne 0 comme valeur aux propriétés margin et padding de <body> :

body
{
font: 11px verdana, sans-serif;
background: #AFA99B url("fond.jpg") top left no-repeat;
margin: 0;
padding: 0;
}

Ensuite, on s'occupe du style des listes.

Il faut que ma liste principale s'affiche de façon horizontale et ne présente pas de puces comme elle le fait par défaut, et que ses items <li> soient alignées horizontalement. Il y a deux méthodes répandues pour faire ça : utiliser display: inline ou faire flotter les éléments <li>. Chaque méthode a ses avantages et ses inconvénients. J'ai choisi de faire flotter les éléments. Pour cet exemple, comme l'image de fond que j'ai collé sur <body> fait 644px, je donne une largeur de 644px à ma liste. Important également, les bords, les marges et le remplissage à 0 pour homogénéiser le rendu en fonction des différents navigateurs :

#menuDeroulant
{
width: 644px;
list-style-type: none;
margin: 0;
padding: 0;
border: 0;
}
#menuDeroulant li
{
float: left;
margin: 0;
padding: 0;
border: 0;
}

Occupons-nous de nos sous-menus. Commençons par empêcher l'affichage des puces en utilisant list-style-type: none; sur les éléments <ul> de nos sous-menus. Comme nous avons fait flotter à gauche les éléments <li> parents de nos sous-menus, ces derniers vont hériter de cette propriété. Il faut que nous empêchions les éléments <li> de notre sous-menu de flotter, pour cela on utilise float: none :

#menuDeroulant .sousMenu
{
list-style-type: none;
margin: 0;
padding: 0;
border: 0;
}
#menuDeroulant .sousMenu li
{
float: none;
margin: 0;
padding: 0;
border: 0;
}

Nous sommes maintenant confrontés à un problème si nous voulons que notre menu ait une apparence correcte dans IE5 Mac.
Dans la majorité des navigateurs récents, lorsqu'un élément flottant contient un autre élément, la largeur de l'élément flottant conteneur est réduite à la largeur de son contenu.
Dans IE5 Mac, il y a un bug qui fait que lorsqu'un élément de type block et de largeur automatique (width: auto) est contenu dans un élément flottant, IE5 Mac fait prendre à cet élément et à son conteneur flottant toute la largeur disponible dans la fenêtre du navigateur.
Dans notre cas, les éléments conteneur flottants sont les éléments <li> de notre liste de premier niveau. Les éléments contenus de type block sont les éléments <li> de nos listes de second niveau. Par défaut leur largeur est définie en automatique si nous ne la spécifions pas.
La seule solution que j'ai trouvée à l'heure actuelle c'est de donner une largeur explicite aux éléments <li> de notre menu de premier niveau qui sera héritée par nos menus de second niveaux.

#menuDeroulant li
{
float: left;
width: 150px;
margin: 0;
padding: 0;
border: 0;
}

Maintenant, je vais m'occuper de l'apparence du menu déroulant en donnant du style aux liens contenus dans nos listes. Je vais faire passer les éléments <a> de leur type inline par défaut à un type block via display: block; et styler les différents états des liens. En passant on règle un problème relatif à Internet Explorer 7 en ajoutant une hauteur de 1% à nos liens.

Pour donner un fond transparent, il faut créer dans un logiciel de création d'image un rectangle de couleur uni avec un niveau de transparence alpha à 60% par exemple et l'enregistrer au format PNG. Puis je le place en tant qu'image de fond des liens de nos sous-menus :

#menuDeroulant li a:link, #menuDeroulant li a:visited
{
display: block;
height: 1%;
color: #FFF;
background: #3B4E77;
margin: 0;
padding: 4px 8px;
border-right: 1px solid #fff;
text-decoration: none;
}
#menuDeroulant li a:hover { background-color: #F2462E; }
#menuDeroulant li a:active { background-color: #5F879D; }

#menuDeroulant .sousMenu li a:link,
#menuDeroulant .sousMenu li a:visited
{
display: block;
color: #FFF;
margin: 0;
border: 0;
text-decoration: none;
background: transparent url("fondTR.png") repeat;
}
#menuDeroulant .sousMenu li a:hover
{
background-image: none;
background-color: #F2462E;
}

Pour plus de raffinement et dans l'esprit de la transparence, nous pouvons donner un bord supérieur et un bord droit transparents aux éléments <li> de nos sous-menus. Comme nous ajoutons 1px de bord droit, il ne faut pas oublier de redéfinir la taille de nos sous-menus en leur retranchant 1px, car la largeur totale d'un élément = width + margin + border + padding :

#menuDeroulant .sousMenu li
{
float: none;
margin: 0;
padding: 0;
border: 0;
width: 149px;
border-top: 1px solid transparent;
border-right: 1px solid transparent;
}

Le Menu Magique

Nous allons maintenant profiter des avancées technologiques des navigateurs récents alternatifs.

Masquons nos sous-menus avec display: none :

#menuDeroulant .sousMenu
{
display: none;
list-style-type: none;
margin: 0;
padding: 0;
border: 0;
}

Enfin, appliquons un état :hover à nos éléments <li> de notre liste de premier niveau :

#menuDeroulant li:hover > .sousMenu { display: block; }

Pour éviter un décalage du contenu lorsque le menu est déroulé

Le menu dispose d'une position statique par défaut. En l'état, à chaque fois qu'il sera déroulé, il décalera tout le contenu déclaré après le menu dans le code (X)HTML. Pour y remédier, nous allons lui donner une position absolue :

#menuDeroulant
{
width: 644px;
list-style-type: none;
margin: 0;
padding: 0;
border: 0;
position: absolute;
top: 0;
left: 0;
}

Le résultat

Voir le résultat en ligne dans une nouvelle fenêtre.