C'est un problème très courant qui survient en raison d'un malentendu sur la façon :nth-child()
et le :nth-of-type()
travail. Malheureusement, il n'existe actuellement aucune solution basée sur un sélecteur, car Selectors ne fournit pas de moyen de faire correspondre le nième enfant qui correspond à un sélecteur arbitraire basé sur un modèle tel que les numéros impairs, pairs ou n'importe an+b
où a != 1
et b != 0
. Cela va au-delà des sélecteurs de classe, aux sélecteurs d'attributs, aux négations et aux combinaisons plus complexes de sélecteurs simples.
La :nth-child()
pseudo-classe compte les éléments parmi tous leurs frères et sœurs sous le même parent. Il ne compte pas uniquement les frères et sœurs qui correspondent au reste du sélecteur. De même, la :nth-of-type()
pseudo-classe compte les frères et sœurs partageant le même type d'élément, qui fait référence au nom de la balise en HTML, et non au reste du sélecteur.
Cela signifie également que si tous les enfants du même parent sont du même type d'élément, par exemple dans le cas d'un corps de tableau dont les seuls enfants sont des tr
éléments ou d'un élément de liste dont les seuls enfants sont des li
éléments, alors :nth-child()
et :nth-of-type()
se comportera de manière identique, c'est-à-dire pour chaque valeur de an+b
, :nth-child(an+b)
et :nth-of-type(an+b)
correspondra au même ensemble d'éléments.
En fait, tous les sélecteurs simples dans un sélecteur composé donné, y compris les pseudo-classes telles que :nth-child()
et :not()
, fonctionnent indépendamment les uns des autres, plutôt que de regarder le sous - ensemble d'éléments qui correspondent au reste du sélecteur.
Cela implique également qu'il n'y a pas de notion d'ordre parmi les sélecteurs simples au sein de chaque sélecteur composé individuel 1 , ce qui signifie par exemple que les deux sélecteurs suivants sont équivalents:
table.myClass tr.row:nth-child(odd)
table.myClass tr:nth-child(odd).row
Traduits en anglais, ils signifient tous les deux:
Sélectionnez n'importe quel tr
élément qui correspond à toutes les conditions indépendantes suivantes:
- c'est un enfant impaire de son parent;
- il a la classe "row"; et
- c'est un descendant d'un
table
élément qui a la classe "myClass".
(vous remarquerez mon utilisation d'une liste non ordonnée ici, juste pour ramener le point à la maison)
Comme il n'y a actuellement pas de solution CSS pure, vous devrez utiliser un script pour filtrer les éléments et appliquer des styles ou des noms de classes supplémentaires en conséquence. Par exemple, ce qui suit est une solution de contournement courante utilisant jQuery (en supposant qu'il n'y a qu'un seul groupe de lignes rempli d' tr
éléments dans le tableau):
$('table.myClass').each(function() {
// Note that, confusingly, jQuery's filter pseudos are 0-indexed
// while CSS :nth-child() is 1-indexed
$('tr.row:even').addClass('odd');
});
Avec le CSS correspondant:
table.myClass tr.row.odd {
...
}
Si vous utilisez des outils de test automatisés tels que Selenium ou que vous traitez du HTML avec des outils tels que lxml, bon nombre de ces outils permettent à XPath comme alternative:
//table[contains(concat(' ', @class, ' '), ' myClass ')]//tr[contains(concat(' ', @class, ' '), ' row ')][position() mod 2)=1]
D'autres solutions utilisant différentes technologies sont laissées comme exercice au lecteur; ceci n'est qu'un bref exemple artificiel à titre d'illustration.
Pour ce que ça vaut, il y a une proposition pour une extension de la :nth-child()
notation à ajouter au niveau 4 des sélecteurs dans le but spécifique de sélectionner chaque nième enfant correspondant à un sélecteur donné. 2
Le sélecteur par lequel filtrer les correspondances est fourni comme argument de :nth-child()
, encore une fois en raison de la façon dont les sélecteurs fonctionnent indépendamment les uns des autres dans une séquence dictée par la syntaxe de sélecteur existante. Donc, dans votre cas, cela ressemblerait à ceci:
table.myClass tr:nth-child(odd of .row)
(Un lecteur astucieux constatera tout de suite que cela devrait être :nth-child(odd of tr.row)
au contraire, puisque les sélecteurs simples tr
et :nth-child()
fonctionnent indépendamment les uns des autres aussi bien. Ceci est l' un des problèmes avec les pseudo-classes fonctionnelles qui acceptent sélecteurs, une boîte de Pandore que je plutôt pas ouvert au milieu de cette réponse. Au lieu de cela, je vais partir avec l'hypothèse que la plupart des sites n'auront pas d'autres éléments que des tr
éléments en tant que frères et sœurs les uns des autres dans un corps de tableau, ce qui rendrait l'une ou l'autre des options fonctionnellement équivalentes.)
Bien sûr, étant une toute nouvelle proposition dans une toute nouvelle spécification, cela ne sera probablement pas mis en œuvre avant quelques années. En attendant, vous devrez vous en tenir à l'utilisation d'un script, comme ci-dessus.
1 Si vous spécifiez un type ou un sélecteur universel, il doit venir en premier. Cependant, cela ne change pas fondamentalement le fonctionnement des sélecteurs; ce n'est rien de plus qu'une bizarrerie syntaxique.
2 Cela a été proposé à l'origine car :nth-match()
, cependant, parce qu'il ne compte toujours un élément que par rapport à ses frères et sœurs, et non à tous les autres éléments qui correspondent au sélecteur donné, il a depuis été réorienté depuis 2014 comme une extension de l'existant :nth-child()
.