En termes d’ergonomie, on conseille souvent d’implanter dans ses tables HTML des « pyjamas », c’est-à-dire des couleurs de lignes alternées. Cela permet de faciliter la lecture : l’œil peut plus facilement suivre les lignes.
En CSS 3, il existe une solution tout à fait satisfaisante, utiliser la pseudo classe nth-child
qui permet de sélectionner le nième fils d’un nœud DOM.
Cette pseudo classe accepte une syntaxe du type an+b où n représente le fils sélectionné et accepte deux mots clefs odd (pour les fils dont l’index est impair) et even (pour les fils dont l’index est pair). On a :
nth-child(2n)
correspond ànth-child(even)
nth-child(2n+1)
correspond ànth-child(odd)
On peut donc directement utiliser cette pseudo classe pour sélectionner les lignes paires et impaires (c’est-à-dire les nœuds de type TR) en fonction de la parité de leur index. Il est bien sûr possible d’utiliser nth-child pour mettre en place des couleurs alternées de plus de 2 couleurs voire mettre en place des couleurs de colonnes alternées (en utilisant la balise COL
).
Si on veut ne mettre des couleurs de lignes alternées qu’aux lignes situées dans les balises TBODY
, et pas sur celles qui sont situées dans le THEAD
et le TFOOT
, on utilisera des CSS telles que :
TBODY tr:nth-child(odd) {
background-color: #aaa;
}
TBODYtr:nth-child(even) {
background-color: #9CF;
}
Une feuille de style de ce type marche parfaitement sur les navigateurs récents… sauf sous Internet Explorer, où même dans la version IE9, le sélecteur nth-child n’est toujours pas implanté.
Du coup, il existe même des librairies javascript pour résoudre de façon générique ce problème, par exemple : http://selectivizr.com/.
Mise en œuvre d’une solution pour Internet Explorer
Nous nous contenterons ici, d’une solution ad hoc simplifiée.
Pour Internet Explorer, une solution est d’associer un attribut class différent selon qu’il s’agit d’une ligne paire ou d’une ligne impaire. Par exemple:
<TR class="impair"><TD>…</TD></TR>
<TR class="pair"><TD>…</TD></TR>
<TR class="impair"><TD>…</TD></TR>
…
Il est même plus simple de n’associer que les attributs pairs (ou impairs) et de faire gérer l’autre valeur par le style par défaut.
TR { background-color: #9CF }
Et
TR.impair { background-color: #AAA;}
En ne marquant que les lignes impaires
<TR class="impair"><TD>…</TD></TR>
<TR><TD>…</TD></TR>
<TR class="impair"><TD>…</TD></TR>
…
On obtient alors un affichage identique à celui procuré en CSS 3 par :
TR:nth-child(odd) { background-color: #9CF }
TR:nth-child(even) { background-color: #AAA }
Il reste à déterminer comment marquer ces lignes impaires. Si la table HTML est générée par le serveur (par exemple une taglib), le plus simple est que le serveur génère lui-même les attributs class correspondant.
Par contre, si c’est javascript qui doit rajouter le « pyjama » à une table préexistante, il faut itérer sur les lignes pour rajouter le nom de class correspondant. Noter que çà veut dire que dans un premier temps, la table s’affiche sans pyjama, celui-ci étant créé plus tard via javascript.
Cela donne par exemple une boucle telle que :
function createPyjama(table) {
// Récupération des tbodies de la table
var tableBodies = table.getElementsByTagName("TBODY
");
// Itération sur chaque
TBODY
for (var i = 0; i < tableBodies.length; i++) {
var tableRows = tableBodies[i].rows;
// Marquage des lignes impaires
for (var j = 1; j < tableRows.length; j+=2) {
tableRows[j].className += " impair";
}
}
}
Une telle méthode donne le résultat attendu si la table à laquelle elle s’applique ne possède pas des rangées possédant déjà la classe impair.
Gérer les suppressions de ligne
Malheureusement, si on donne la possibilité de modifier au runtime la table en question, par exemple en supprimant ou en rajoutant une ligne, il est nécessaire de mettre à jour le « pyjama » (sous Internet Explorer, les browsers supportant les CSS 3 n’ont pas ce problème puisque le pseudo sélecteur nth-child fait tout le travail).
Dans ce cas, si on a ajouté (ou supprimé) un nombre impair de lignes, il est nécessaire de recalculer les attributs class de la table pour toutes les lignes suivantes.
Version simplifiée : on recalcule toute la table
La solution la plus simple consiste à recalculer l’intégralité du pyjama. Il suffit de prendre la précaution pour les lignes qui doivent être modifiées de :
- Retirer la class impair des lignes qui le contenait
- Ajouter la class impair aux autres lignes
Cela donne par exemple :
function alternate(table) {
var tableBodies = table.getElementsByTagName("
TBODY
");
for (var i = 0; i < tableBodies.length; i++) {
var tableRows = tableBodies[i].rows;
var pair=true;
for (var j = 0; j < tableRows.length; j++) {
var theTableRow=tableRows[j];
var theClassName=theTableRow.className;
if ( pair ) {
if ( (theClassName.indexOf('impair') != -1) ) {
theTableRow.className = theClassName.replace('impair', '');
}
} else {
if ( (theClassName.indexOf('impair') == -1) ) {
theTableRow.className += " impair";
}
};
pair=!pair;
}
}
}
Version optimisée
Pour être encore plus efficace, l’idée est de ne modifier que les lignes qui sont situées en dessous de la ligne supprimée. On peut ainsi ajouter un argument qui est l’index de la première ligne supprimée (ajoutée). Cela permet de ne pas refaire le calcul pour les premières lignes, qui n’ont pas besoin d’être touchées. Sur des navigateurs très peu performants comme Internet Explorer 6, cela peut faire une réelle différence.
La première étape est de déterminer l’index de la ligne supprimée. Il ne semble pas avoir de méthode standard pour déterminer quel est l’index d’un nœud DOM parmi tous ses frères (les autres nœuds fils du même nœud père). On peut enchaîner des appels à nextSibling
pour itérer de ligne en ligne (balise TR
) mais çà n’est pas forcément le plus performant. Cela peut poser problème par exemple quand on sélectionne une ligne à la souris pour la détruire. Il est facile de récupérer le nœud DOM TR correspondant à la ligne cliquée via le handler d’événement. Mais comment connaître son index parmi tous les TR ? En plus, la méthode indexOf, introduite avec Javascript 1.6 n’est disponible que sur une instance d’Array javascript, pas sur une HTMLCollection (les nœuds fils d’un nœud DOM forment une HTMLCollection, pas une Array). Et de toutes façons indexOf n’est pas disponible sous Internet Explorer 6.
Heureusement, il existe deux attributs supportés par la plupart des navigateurs et en tout cas, par les différentes versions d’Internet Explorer. Il s’agit des attributs rowIndex
et sectionRowIndex
. Ici, c’est la propriété sectionRowIndex
qui nous intéresse : elle donne l’index de la ligne au sein de la section courante, ici un TBODY
.
On peut donc implanter une méthode utilisant cet index pour ne recalculer que le « pyjama » des lignes suivantes. Mais attention, il faut tenir compte du fait qu’une table peut avoir plusieurs balises TBODY
. La parité des lignes doit donc être transmise d’un TBODY
à l’autre.
Au chargement, les lignes ont la couleur définie par la CSS s’appliquant par défaut sur les TR
:
TR { color: black ; background-color: #9CF }
Il y a aussi la déclaration de la classe impair :
TR.impair { color: white ; background-color: #aaa }
C’est seulement via javascript que l’attribut class sera positionné à impair. Cela veut dire que si javascript n’est pas actif, les lignes restent de la couleur d’origine. Il vaut donc mieux que le style par défaut correspond à la couleur la plus claire. On garantit ainsi que la table sera plus facilement lisible même si javascript est désactivé.
Démonstration
Dans cette démonstration, un clic sur une cellule de la table supprime la ligne correspondante.
La table comporte un THEAD
, un TFOOT
et deux balises TBODY
.
La démo comporte 2 boutons permettant de supprimer par programme la deuxième et la troisième ligne.