La vengeance des menus déroulants
Par Patrick Griffiths et Dan Webb
Notre premier article sur les menus déroulants «Suckerfish dropdowns», publié sur A List Apart, a montré avec un certain succès qu'on pouvait mettre en œuvre des menus déroulants sur une base de CSS : ils se sont révélés légers, accessibles, et fonctionnaient dans Internet Explorer grâce à leur imitation de la pseudo-classe :hover
.
Vous les avez aimés, les revoilà : ils sont plus accessibles, encore plus légers (seulement 12 lignes de Javascript), bénéficient d'une compatibilité accrue (ils fonctionnent sous Opera et Safari sans devoir utiliser le moindre hack) et peuvent avoir plusieurs niveaux de profondeur.
Menus déroulants à un niveau
Bon, on ne va pas tergiverser. Le HTML auquel on a affaire va ressembler à ceci :
<ul id="nav"> <li><a href="/traduction/deroulants#">Squaliformes</a> <ul> <li><a href="/traduction/deroulants#">Echinorhinidés</a></li> <li><a href="/traduction/deroulants#">Oxynotidés</a></li> <li><a href="/traduction/deroulants#">Squalidés</a></li> </ul> </li> <li><a href="/traduction/deroulants#">Orectolobiformes</a> <ul> <li><a href="/traduction/deroulants#">Parascylliidés</a></li> <li><a href="/traduction/deroulants#">Brachaeluridés</a></li> <li><a href="/traduction/deroulants#">Orectolobidés</a></li> <li><a href="/traduction/deroulants#">Stégostomatidés</a></li> <li><a href="/traduction/deroulants#">Hémiscylliidés</a></li> </ul> </li> <!-- etc. --> </ul>
Voilà : une bonne vieille liste non numérotée, bien structurée.
Pour démarrer, nous avons besoin d'un style de base :
#nav, #nav ul { padding: 0; margin: 0; list-style: none; } #nav a { display: block; width: 10em; } #nav li { float: left; width: 10em; }
Remarquez qu'il vaut mieux indiquer une largeur (width
) dans le sélecteur #nav li
, sinon Opera va péter un câble. Rappelez-vous aussi que dès qu'on fait flotter des éléments, le contenu situé en-dessous du menu déroulant doit être dégagé (avec clear: left
).
Bien sûr, il faut cacher les listes que l'on veut voir se dérouler, mais pour avoir une meilleure accessibilité on évitera d'utiliser display: none
, qui dissimule des éléments à certains lecteurs d'écran (comme on peut fréquemment le constater dans les articles portant sur les remplacements d'images). Vous pensez peut-être qu'il y a mille façons de régler ce problème, mais après avoir expérimenté de façon exhaustive tout ce qui est width
, height
, margin
, top
ou clip
à travers un grand nombre de navigateurs, la meilleure solution (pour ce qui est des listes à plusieurs niveaux, en tous cas) réside dans une manipulation de la propriété left
.
Les spécifications CSS affirment que pour une boîte positionnée de façon absolue, les valeurs top
, right
, bottom
et left
doivent créer un décalage par rapport au bloc qui la contient. Malheureusement, Opera décide de décaler les boîtes positionnées en absolu par rapport à la page, ce qui explique pourquoi les premiers "Suckerfish Dropdowns" ne fonctionnaient pas sous Opera : ils s'appuyaient sur les propriétés top
et left
avec des longueurs explicites.
Donc, à la place de display: none
, on utilise left: -999em
pour expédier la liste déroulante hors de notre champ de vision, et ensuite left: auto
(plutôt que left: 0
) pour la ramener en position :
#nav li ul { position: absolute; width: 10em; left: -999em; } #nav li:hover ul { left: auto; }
Le problème est entièrement réglé pour tous les navigateurs qui supportent la pseudo-classe :hover
, mais pour Internet Explorer il va falloir balancer le Javascript "Suckerfish" :
sfHover = function() {
var sfEls = document.getElementById("nav").getElementsByTagName("LI");
for (var i=0; i<sfEls.length; i++) {
sfEls[i].onmouseover=function() {
this.className+=" sfhover";
}
sfEls[i].onmouseout=function() {
this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
}
}
}
if (window.attachEvent) window.attachEvent("onload", sfHover);
En gros, ce code va appliquer la classe "sfhover
" aux items ("li
") de la liste ("ul
") nommée ("id
") "nav
", lorsque ces items sont survolés par la souris. Cette classe est ensuite retirée en utilisant une expression régulière lorsque la souris quitte la zone.
Donc, maintenant que les Suckerfish sont bourrés de nouvelles classes, l'étape suivante consiste simplement à répéter le sélecteur :hover
avec les sélecteurs de classe "sfhover
" :
#nav li:hover ul, #nav li.sfhover ul { left: auto; }
Et voilà. Vous avez un beau menu déroulant à un niveau.
Menus déroulants à plusieurs niveaux
L'article d'origine sur les "Suckerfish Dropdowns" ne s'intéressait qu'aux menus déroulants à un niveau, mais en étendant un peu la logique de cascade, il s'avère tout à fait possible de créer également des menus déroulants à plusieurs niveaux en CSS. Et, contrairement au code Javascript "Suckerfish" d'origine, la fonction "sfhover
" applique le comportement à tous les items ("li
") descendant de "nav
", pas seulement aux enfants directs : donc, les menus déroulants à plusieurs niveaux sont désormais tout à fait utilisables, même avec Internet Explorer.
Pour commencer, disons que nous avons affaire à une structure de liste avec davantage de niveaux, comme celle-ci :
<ul id="nav"> <li><a href="/traduction/deroulants#">Squaliformes</a> <ul> <li><a href="/traduction/deroulants#">Echinorhinidés</a> <ul> <li><a href="/traduction/deroulants#">Echinorhinus brucus</a></li> <li><a href="/traduction/deroulants#">Echinorhinus cookei</a></li> </ul> </li> <li><a href="/traduction/deroulants#">Oxynotidés</a> <ul> <li><a href="/traduction/deroulants#">Oxynotus paradoxus</a></li> <li><a href="/traduction/deroulants#">Oxynotus bruniensis</a></li> <li><a href="/traduction/deroulants#">Oxynotus caribbaeus</a></li> <li><a href="/traduction/deroulants#">Oxynotus centrina</a></li> <li><a href="/traduction/deroulants#">Oxynotus japonicus</a></li> </ul> </li> <li> <!-- etc. --> </li> </ul> </li> <li><a href="/traduction/deroulants#">Orectolobiformes</a> <!-- etc. --> </li> </ul>
Nous allons devoir rajouter quelques petites choses à la méthode originale. Tout d'abord, la liste de troisième niveau (dans notre exemple "Echinorhinus brucus", "Echinorhinus cookei", etc) doit se dérouler sur le côté de l'item de liste qui lui correspond (en l'occurence "Echinorhinidés"), il faut donc rajouter cette règle qui s'appliquera à tous les menus déroulants après le premier :
#nav li ul ul {
margin: -1em 0 0 10em;
}
Comme on ne peut pas préciser explicitement le point de départ supérieur de nos boîtes en position absolue, elles se situeront sous la ligne de l'item de liste survolé, ce qui explique pourquoi le niveau de listes suivant doit être placé à -1em. Mais cela ne suffira pas à élever suffisamment les menus pour qu'ils arrivent au niveau de l'item de liste correspondant, parce que les hauteurs de ligne par défaut sont supérieures à 1em (souvent 1.2em), on doit donc rajouter un petit quelque chose à la règle initiale de l'élément ul
:
#nav, #nav ul { padding: 0; margin: 0; list-style: none; line-height: 1; }
À cause de l'effet de cascade qui veut que l'affichage de la liste de deuxième niveau révèle également celle du troisième niveau, il faut aussi cacher explicitement cette liste de troisième niveau (souvenez-vous que nous devons répéter la pseudo-classe :hover
avec la classe .sfhover
) :
#nav li:hover ul ul, #nav li.sfhover ul ul { left: -999em; }
Bien sûr, on peut passer outre cette règle de façon à afficher lorsque l'item de liste correspondant sera survolé, en étendant le principe d'affichage du menu déroulant (qui donnait ceci, avec le menu à un seul niveau : #nav li:hover ul, #nav li.sfhover ul { left: auto; }
) :
#nav li:hover ul, #nav li li:hover ul, #nav li.sfhover ul, #nav li li.sfhover ul { left: auto; }
Et avec ça nous obtenons un solide menu déroulant à deux niveaux.
En suivant la même logique, vous pouvez fabriquer autant de niveaux de menus que vous le voulez :
Pour les menus déroulants à trois niveaux :
#nav li:hover ul ul, #nav li:hover ul ul ul, #nav li.sfhover ul ul, #nav li.sfhover ul ul ul { left: -999em; } #nav li:hover ul, #nav li li:hover ul, #nav li li li:hover ul, #nav li.sfhover ul, #nav li li.sfhover ul, #nav li li li.sfhover ul { left: auto; }
Et pour quatre niveaux, même si c'est peu probable...
#nav li:hover ul ul, #nav li:hover ul ul ul, #nav li:hover ul ul ul ul, #nav li.sfhover ul ul, #nav li.sfhover ul ul ul, #nav li.sfhover ul ul ul ul { left: -999em; } #nav li:hover ul, #nav li li:hover ul, #nav li li li:hover ul, #nav li li li li:hover ul, #nav li.sfhover ul, #nav li li.sfhover ul, #nav li li li.sfhover ul, #nav li li li li.sfhover ul { left: auto; }
Exemples
À ce stade, vous avez peut-être déjà regardé les exemples très dépouillés à un, à deux, et à trois niveaux, ce qui est sans doute l'idéal pour observer le code source de la manière la plus claire et basique possible; mais bien entendu vous pouvez rendre tout ceci plus agréable à l'œil. Vous pouvez même en faire un menu vertical au lieu d'un horizontal.
http://www.htmldog.com/articles/suckerfish/dropdowns/