ORdI&ORbI n'Web...

Accueil

Menu horizontal de taille fixe : table ou ul ?

Le but est de produire un menu dans lequel chaque article est formaté comme une case d'un tableau horizontal.

Voici notre cahier des charges :

  • longueur fixe pour les cases,
  • effet de rollover sur la totalité de la case,
  • les cases peuvent contenir ou non des liens,
  • cross-browser IE6 et Mozilla,
  • adaptabilité si possible du menu à différents contextes et à différentes tailles de contenu sans devoir toucher au code.

Nous essayerons 5 techniques pour réaliser ce menu :

Menu en élément "table" sans JavaScript

Exemple :

Votre avis Accueil Item un peu long pour voir

Le code HTML ou XHTML

<table class="tablomenu" cellspacing="0">
 <tr>
  <td><span class="menu">Votre avis</span></td>
  <td><a href="#" class="menu">Accueil</a></td>
  <td><a href="item.html" class="menu">Item un peu long pour voir</a></td>
 </tr>
</table>

Le cellspacing="0" est nécessaire pour IE5 Mac qui sinon laisse un espace entre les cellules malgré le style "border-collapse:collapse".

Les styles css

Les styles pour le tableau "table" :

table.tablomenu{
border-collapse:collapse;
border:1px solid #B8CF9E;
background-color:#E3F5EB;
font-size:.9em;
}
table.tablomenu td {
padding:0;
margin:0;
border:solid #B8CF9E;
border-width:0 1px;
text-align:center;
width:113px;
}
Les styles pour les éléments du menu :

Comme on veut un rollover sur toute la "case", on met les liens "a" en "display:block" de façon à les étendre en largeur à toute la cellule.


table.tablomenu *.menu {
  display:block;
  padding:4px;
  height:2.5em;
}

a.menu:hover {
  color: #338333;
  background-color: #CCFFCC;
}

On remarque qu'on indique la hauteur ("height") du bloc. Sans cela sa hauteur générée serait celle de son contenu et varierait donc en fonction du nombre de lignes du lien.

Ci-dessous, une image du rollover en rouge que l'on obtiendrait sans spécifier de hauteur :

visuel du rendu dans IE6

On serait tenter de mettre height:100%, afin que la hauteur du bloc s'étende à la hauteur de la case du tableau. C'est ce que j'avais fait dans une premiere version de cette étude. Ceci fonctionnait avec Firefox 1.0. Mais les spécifications du W3C disent qu'en absence d'indication spécifique de hauteur du bloc parent, la hauteur d'un bloc spécifié en % revient à la valeur auto. Voir dans les specification CSS2.1 : Content height: the 'height' property et aussi ce qui en est dit sur sur msdn.microsoft.com. Firefox 1.5 se conforme maintenant à cette spécification.

On est donc obligé de spécifier la hauteur du block en unité "em" qui est relative au corp de la police (en tatonnant pour trouver la bonne valeur correspondant au nombre de lignes maximum que l'on a, dans notre cas:  2 lignes ).

Comportements dans différents contextes et pour différents contenus

Augmentation de la taille de la police dans le navigateur par l'internaute

L'internaute a normalement la possibilité d'augmenter la taille de la police affichée dans son navigateur. Sur IE, cela fonctionne si la police n'a pas été spécifié en px sur la page. Sur la présente page, le contexte fait que cela ne fonctionne pas, voici donc un  popup avec les differents menus dans lequel vous pouvez le faire.

Voici alors le rendu dans Mozilla Firebird :
Rendu dans Mozilla Firebird
Le texte le plus long fait maintenant 3 lignes. Comme Mozilla prend en compte la taille des liens "a" pour dimensionner la rangé de cellule et que ceux ci ont été fixé pour contenir 2 lignes, la ligne excédentaire déborde.

Dans IE6 en taille du texte "plus grande" :
Rendu dans IE6
C'est d'apparence propre mais le rollover se fait toujours sur 2 lignes.

Augmentation de la taille du contenu des éléments du menu

Dans ce cas on a les mêmes effets de débordements que ci-dessus, mais à corp de police constant.

Dans un bloc conteneur plus petit que le menu

On figure en tirets bleus les limite d'un bloc <div> de 150px

Votre avis Accueil Item un peu long pour voir

Menu en element "table" avec JavaScript

La solution précédente ne s'adapte pas automatiquement au changement du nombre de lignes de texte dans les cases puisque dans IE le rollover ne se fera pas sur la totalité de la cellule. Appelons JavaScript à la rescousse.

Pour IE5+ PC, il existe une solution en une ligne qui consiste à se servir d'une extension propriétaire d'IE : expression(). Cela permet d'associer une propriété de style à une expression JScript. Il suffit juste de rajouter à la balise <tr> un id="nav" pour pouvoir la selectionner par un document.getElementById("nav"). Ensuite dans la feuille de style on indique simplement :


  height:expression(
    document.getElementById('nav').offsetHeight
   );

Pour une solution standard fonctionnant dans tous navigateurs avec JavaScript, je reprend ici en l'adaptant un code JavaScript donné par Patrick Griffiths et Dan Web dans un article d'ALISTAPART.

Il s'agit en utilisant le DOM d'attacher un événement "onmouseover" à l'élément TD du tableau pour changer sa "class" au survol de la souris.

Exemple :

Le code HTML ou XHTML

C'est quasiment le même que précédemment. On a remplacé la class="tablomenu" par class="tablomenuJS" et on a ajouté un id="nav" dans la balise  <tr>.

Les styles css

Les styles pour le tableau "<table>" :


table.tablomenuJS {
border-collapse:collapse;
border:1px solid #B8CF9E;
background-color:#E3F5EB;
font-size:.9em;
}

table.tablomenuJS td {
padding:0;
margin:0;
border:solid #B8CF9E;
border-width:0 1px;
width:105px;
text-align:center;
vertical-align:middle;
padding:4px;
}

Il n'y a plus besoin de mettre les liens en block. Cela permet incidemment de centrer verticalement les textes dans les cellules. Ce n'est en effet pas possible avec la technique ci-dessus lorqu'il y a plusieurs lignes pour un item de menu. Si on n'avait qu'une ligne par item de menu on pourrait utiliser la propriété line-height comme expliqué ici[en].

Les styles pour le rollover en CSS :
.over{
color: #E3F5EB;
background-color: #EE5555;
cursor:pointer;
}
.over a{ color: #E3F5EB; }
Le javascript pour le rollover

C'est un JavaScript quasiment repris d'ALA

startList = function() {
 if (document.getElementById) {
  navRoot = document.getElementById("nav");
  for (i=0; i<navRoot.childNodes.length; i++) {
   node = navRoot.childNodes[i];
   if (node.nodeName=="TD" && node.childNodes[0].nodeName=="A") {
    node.onmouseover = function() {
     this.className+="over";
    }
    node.onclick = function(){
     window.location.href = this.childNodes[0].getAttribute('href');
    }
    node.onmouseout=function() {
     this.className=this.className.replace("over", "");
    }
   }
  }
 }
}
window.onload=startList;

Menu en liste "ul" technique "float: left"

 

Le code HTML ou XHTML

<ul class="listMenu">
 <li class="first" ><span class="menu">Votre avis</span></li>
 <li><a href="#" class="menu">Accueil</a></li>
 <li><a href="item.html" class="menu">Item un peu long pour voir</a></li>
</ul>
<div class="spacer">&nbsp;</div>

Les styles css

Les styles pour la liste :
ul.listMenu {
padding:0;
margin:0;
text-align:center;
font-size:.9em;
}
ul.listMenu li {
list-style-type: none;
float:left;
}
.spacer{clear:left}
Les styles pour les éléments du menu :
ul.listMenu li.first .menu{
border-width: 1px;
}
ul.listMenu .menu {
display:block;
border:solid #B8CF9E;
border-width: 1px 1px 1px 0;
background-color:#E3F5EB;
padding:4px;
height:2.5em;
width:105px;
}

On rencontre le même problème que pour le menu en table, il faut indiquer dans le block la hauteur du menu. Si on ne le fait pas voici en image ce que ça donne :


Comportements dans différents contextes

Augmentation de la taille du texte dans le navigateur par l'utilisateur

On retrouve exactement les mêmes phénomènes que pour le menu en "table" décrit auparavant. Cela donne donc dans IE6 :

Dans un bloc conteneur plus petit que le menu
 

Vous pouvez aussi observer ce comportement en faisant varier la taille du popup avec les differents menus.

On pourrait émuler le comportement du menu en "table" en donnant des % au width, mais on est moins précis qu'avec le tableau car les bordures sont toujours précisé en pixels alors que la taille du contenu est en %. L'experience montre que les tableaux <table> s'adaptent mieux aux tailles relatives. Enfin dans Mozilla on a un bug : parfois, selon la largeur total, un espace d'un pixel apparaît entre les éléments.

Menu en liste "ul" technique "float: left" avec JavaScript

On va ajuster la hauteur de chacune des cases du menu à la hauteur de la plus grande des cases.

 

Le code HTML ou XHTML

Même code que précédemment.

Les styles

Même styles que précédemment sauf qu'on ne précise plus le "height" pour la hauteur des texte du menu.

Le code Javascript

function equalizeListHeight() {   
  var paddingAndBorder = 10;//indication en dur du padding et de la bordure
  if (document.getElementById) {
    navRoot = document.getElementById("navFloat");
    /* recherche de la hauteur maximale du texte */
    max_height = 0;
    for (i=0; i < navRoot.childNodes.length; i++){
      if (navRoot.childNodes[i].nodeName == "LI") {
        node = navRoot.childNodes[i];
        if (max_height < parseInt(node.childNodes[0].offsetHeight)){
          max_height = node.childNodes[0].offsetHeight;
        }
      }
    }
    /*test si Microsoft-box model*/
    node.childNodes[0].style.height= max_height+'px';
    if(node.childNodes[0].offsetHeight != max_height){
      max_height -= paddingAndBorder;
    }
    /* application de la hauteur maximale au style pour les éléments du menus */
    for (i=0; i<navRoot.childNodes.length; i++) {
      if (navRoot.childNodes[i].nodeName == "LI") {
        node = navRoot.childNodes[i];
        node.childNodes[0].style.height= max_height+'px';
      }
    }
  }
}

L'inconvénient de ce code est qu'on est obligé d'indiquer en dur la valeur des paddings et des borders.

Menu en list "ul" en utilisant "display:table" et "display:table-cell"

Attention ces valeurs de la propriété display adjoignent une sémantique de table aux éléments sur lesquels ils sont appliqués cf spec CSS2 ( la specification CSS2.1, en Working draft depuis le 13 Juin 2005, remplacera la CSS2 et apporte des précisions importantes sur ces valeurs de règle de style ). Ici, c'est l'affichage à l'écran et non la sémantique table qui nous intérresse. C'est pourquoi, il me semble nécessaire d'inclure ces déclarations dans un bloc de type "@media screen, print" afin de garder la sémantique de type liste pour les navigateurs auraux et autre types d'appareils. Malheureusement cette précaution est actuellement (sept 2004) inutile puisque les lecteur d'écrans actuels ne tiennent pas compte des directives @medias.

Code HTML ou XHTML

C'est quasiment le même code que dans la technique des float:left.

Les styles CSS

@media screen, print{
ul.listMenuCell {
display:table;
padding:0;
margin:0;
border-collapse:collapse;
font-size:.9em;
}

ul.listMenuCell li {
display:table-cell;
list-style-type: none;
margin:0;
padding:0;
border:1px solid #B8CF9E;
background-color:#E3F5EB;
width:113px;
text-align:center;
}
}

Dans Mozilla, la liste se présente et se comporte alors exactement comme une table (Dans notre contexte,toutefois, la "table" est parfois coupée en deux, et redeviens normal au rechargement de la page. Cela n'apparaît pas si on remplace le texte seul de la première cellule par un lien). Dans IE6 PC, les cases sont empilés les unes sur les autres.

Selon les spécification CSS2 :

"Des langages de document autres que HTML peuvent ne pas contenir tous les éléments du modèle de table de CSS2. Pour ceux-ci, on doit supposer l'existence des éléments "absents" pour que le modèle puisse fonctionner."

C'est pourquoi, dans nos déclaration css, il n'y a pas besoin d'élément qui joue le rôle de rangé. Les agents utilisateur doivent les "supposer".

Conclusion

La technique des listes "ul" avec li en float:left nous permet de reproduire la même présentation qu'une mise en page en "table" dans un contexte connu d'utilisation (corp de police normal, et taille du menu non contraint par le bloc conteneur). Par contre, quand on ne connaît pas à l'avance le nombre de lignes que va prendre le texte (cas d'un menu issu d'une base de données) la mise en forme en tableau s'adapte mieux. Dans les deux cas "table" ou "ul", on peut ajouter du JavaScript pour que la présentation soit parfaite. Le seul avantage qui reste alors au tableau est que l'on peut centrer le texte dans la cellule.

Toutefois les listes "ul" présentent l'avantage de posséder la sémantique recommandée pour un menu. Ce qui présente un avantage d'un point de vue accessibilité et d'un point standardisation des développements : on devrait s'attendre à ce que tous les menus soit fait en liste "ul", auquels on pourrait appliquer des styles prédéfinis très facilement.

Le meilleurs des deux mondes est atteint en utilisant dans une liste "ul" les couples propriétés-valeurs : display:table et display:table-cell, malheureusement IE6 ne l'implémente pas. C'est bien dommage car c'est cette technique qui permet la plus grande adaptablité du style par rapport au contenu sans avoir besoin d'utiliser du JavaScript !