Les sélecteurs sont, d'après la définition du WC3, des expressions permettant de sélectionner un ou plusieurs éléments dans un arbre. Une de leurs utilisations est d'associer des styles à des éléments. Une feuille de style (CSS) se compose de règles de la forme sélecteur {style}. La norme CSS3, en cours de définition par le W3C[1], définit de nouveaux sélecteurs afin d'offrir encore plus de possibilités de choisir à quels éléments associer une règle.

rappels

Rapide liste des sélecteurs déjà présents dans CSS 1 et 2, dont certains rarement utilisés car pas implémentés dans la bête bleue qui fait le cauchemar de tous développeurs web:

  • * : n'importe que élément
  • E : tous les éléments E
  • E[toto] : tout élément E possédant un attribut toto
  • E[toto="titi"] : tout élément E dont l'attribut toto vaut titi
  • E[foo~="bar"]  : tout élement E dont l'attribut toto contient une liste de valeurs séparées par des espaces, une de ces valeurs étant titi. Par exemple div[class ~="toto"] sélectionnera tous les div dont une des classes est toto
  • E[toto|="titi"] : tout élément E dont l'attribut toto est une liste de valeurs sélarées par des tirets et commença par titi. Par exemple div[lang|="fr"] ramènera les div dont l'attribut lang est "fr-FR" ou "fr-CA" ou "fr-BE", etc
  • E.toto : tout élément E dont la classe est toto. La façon dont la classe est déterminée dépend de la grammaire utilisée. C'est l'attribut class en HTML, mais ça peut être autre chose.
  • E#toto : l'élément E dont l'identifiant unique est toto. Même remarque que pour la classe
  • E:lang(fr) : tout élément dont la langue humaine est le français. La langue peut être déterminée par plusieurs méthodes selon le type de document.
  • E:first-child : tout élement E premier dans la liste des enfants de son père
  • E::first-line : première ligne de contenu de tout E. Utile par exemple en typographie pour mettre en majuscule la première ligne d'un paragraphe
  • E::first-letter : la première lettre de tout élément E. Pour faire des lettrines par exemple.
  • E::before et E::after sont un cas un peu particulier puisqu'ils permettent généralement d'insérer du contenu depuis une CSS avant ou après une balise.
  • E F : tout élément F figurant parmi les descendants de E
  • E > F : tout F fils immédiat de E
  • E + F : tout F immédiatement précédé d'un E
  • E:link : tout élément E source d'un lien qui n'a pas encore été visité
  • E:visited : tout élément E source d'un lien qui a déjà été visité
  • E:focus : utile en HTML pour désigner un élément qui a le focus. Pas sûr que ça serve dans d'autres cas.
  • E:active : idem, tout élément E actif
  • E:hover : idem, tout élément E pendant le survol de la souris

les nouveaux sélecteurs dans CSS3

gestion des espaces de nom

Il n'est pas rare qu'un document XML contienne des éléments de plusieurs namespaces. Un fichier XUL par exemple pourra également contenir du SVG, du XHTML... On peut désormais restreindre un sélecteur à un espace de nom. Il faut pour cela déclarer un alias pour le namespace, puis utiliser la syntaxe alias|selecteur. Par exemple:

@namespace xhtml url(http://www.w3.org/1999/xhtml);
xhtml|image { border: 1px solid black }

affectera une bordure aux images en XHTML mais pas en XUL.

nouveaux sélecteurs d'attribut

CSS introduit 3 nouvelles méthodes pour sélectionner des éléments en fonction de leurs attributs:

  • E[toto^="titi"] : tout élément E dont l'attribut toto commence par "titi"
  • E[toto$="titi"] : tout élément E dont l'attribut toto finit par "titi"
  • E[toto*="titi"] : tout élément E dont l'attribut toto contient "titi"

des pseudos-classes

Les pseudos-classes permettent de sélectionner un élément en fonction de critères externes au document lui-même. Par exemple une interaction avec le lecteur pour les pseudo-classe :focus, :hover.

Pour l'interface utilisateur. De la même façon qu'un élément peut être survolé par le pointeur ou recevoir, le focus, certains éléments peuvent être activés ou désactivés. Ils sont sélectionnables avec les pseudo-classe :enabled et :disabled. Certains élements d'interface peuvent représenter une valeur binaire: bouton radio, case à cocher ou éléments de menu. La pseudo-classe :checked permet de sélectionner ceux qui sont cochés. Enfin E::selection[2] représente la portion d'un élément actuellement sélectionnée.

Sélectionner la cible du lien : une URL peut pointer sur un objet précis à l'intérieur d'un document, au moyen de son identifiant. http://toto.org/index.html#titi pointera vers l'élément d'id "titi" au sein de la page "toto". CSS3 permet de sélectionner cet élément au moyen de la pseudo classe :target. Ainsi

*:target { color : red }

coloriera en rouge l'élément cible de l'url de la page en cours.

Promenons-nous dans le DOM avec:

  • :root qui désigne la racine d'un document
  • E:nth-child(xxx) : tous les éléments E qui sont le xxx fils de leur père. xxx peut être :
    • un chiffre
    • even ou odd : tous les fils pères ou impairs, pratique par exemple pour avoir un style appliqué alternativement aux lignes d'une table
    • an+b, avec a et b deux chiffres. n prendra toutes les valeurs à partir de zéro. Par exemple 2n représente tous les enfants pairs, 2n+1 tous les enfants impairs. a peut être négatif, et le sélecteur ramènera tous les éléments pour lesquels an+b est positif. tr:nth-child(-n+6) sélectionne les 6 première lignes de toutes les tables du document.
  • E:nth-last-child(xxx) reprend le même principe mais en comptant à partir de la fin. :nth-last-child(2) sélectionnera tous les avants-derniers éléments, et :nth-last-child(-n+3) les 3 derniers fils de tout élément.
  • E:last-child est le pendant de E:first-child et synonyme de :nth-last-child(1)
  • E:nth-of-type(xxx) et E:nth-last-of-type(xxx) sélectionnent tous les éléments qui ont xxx - 1 frères de même type qu'eux avant ou après eux. Par exemple img:nth-of-type(2) sélectionnera toutes les 2° images
  • E:first-of-type et E:last-of-type sont des raccourcis dont je vous laisse deviner le sens
  • E:only-child sélectionne tous les fils uniques
  • E:only-of-type renvoie tous les éléments qui sont seuls de leur type parmi les enfants directs d'un élément
  • E:empty représente tous les éléments qui n'ont pas d'enfants (attention, le texte à l'intérieur d'un nœud est un nœud texte).

encore plus...

  • E:not(s), s étant un sélecteur, représente tous les éléments non sélectionnés par icelui
  • E ~ F sera vrai si E et F ont le même père et que F est après E. On peut ainsi sélectionner tous les éléments suivant un élément particulier. Par exemple p:nth-of-type(2) ~ * correspond à tous les éléments après le 2° paragraphe.

nouvelle API

Tous ces sélecteurs sont dès à présents accessibles dans Firefox 3.1. Et ils peuvent aussi être utilisés nativement en javascript, puisqu'une nouvelle API, encore à l'état de brouillon, a été implémentée dans Gecko. Cette API ajoute au DOM deux méthodes permettant de sélectionner des éléments en fonction de ces sélecteurs. DocumentSelector ramène le premier élément répondant à l'expression, querySelectorAll une liste statique de tous les éléments. Ces 2 méthodes peuvent être utilisées sur le document ou sur tout élément. Elles acceptent en premier argument une chaîne contenant le sélecteur. Un second élément, facultatif, permet de passer à la méthode une fonction de résolution d'espace de nom (un NSResolver quoi !). Cela offre une nouvelle alternative entre les basiques getElementById, getElementsByName, getElementsByTagName, getElementsByClassName (depuis FF 3), getElementsByAttribute (uniquement en XUL) et l'utilisation de XPath.

En vrac

  • n'oubliez pas que les sélecteurs peuvent être combinés. Par exemple :first-of-type:last-of-type représente un élément à la fois premier et dernier de son type, c'est équivalent à :only-of-type
  • attention, si en HTML les sélecteurs sont insensibles à la casse, ils le sont en XML.
  • le titre d'un célèbre album de musique classique peut désormais s'écrire :nth-child(7) :nth-child(7) ;-)

Et en conclusion...

C'est bien joli, mais concrètement ça ne va pas servir à grand chose. D'ici à ce que tous les navigateurs qui ne supportent pas ces sélecteurs (y compris Firefox 3.0 qui n'en implémente que 37 sur 43[3] aient disparus, il faudra continuer à utiliser du javascript pour appliquer dynamiquement des styles à des éléments précis. Mais disons qu'on pourra avoir des interfaces qui se dégradent mieux dans certains navigateurs en l'absence de javascript.

Mais à propos de Javascript, la bibliothèque jquery, créé par John Resig, aujourd'hui évangéliste chez Mozilla, implémente depuis quelque temps déjà tous ces sélecteurs. Les connaître est donc utile.

Même si cela ne va pas servir à grand chose dans un premier temps[4], l'avancée de l'implémentation de CSS3 ne peut être qu'une bonne chose, car cela tire tous les navigateurs. Gecko et Webkit semblent lancés dans une course de vitesse, espérons que ça va pousser redmond à suivre pour qu'enfin d'ici une quinzaine d'années, je suis optimiste, CSS3 soit correctement implémenté dans tous les navigateurs et que les développeurs n'aient plus besoin de s'arracher les cheveux pour faire des coins arrondis, des ombres, etc. D'ici là, les créatifs nous auront inventés de nouveaux défis à résoudre (mais j'espère bien être à la retraite).

Allez, tous en chœur: Go Firefox go !

Notes

[1] elle est en état "last call" depuis décembre 2005, c'est à dire à mi-chemin entre le brouillon et la candidature à la reconnaissance comme recommandation. Les processus de décision du W3C semblent longs et complexes :-S

[2] attention c'est un pseudo-élément, donc précédé par 2: et non une pseudo-classe, précédée par un seul : . Les pseudos éléments représentent des propriétés auxquelles on ne peut pas accéder via le DOM (on ne peut pas sélectionner en DOM la première ligne d'un paragraphe, alors qu'on peut savoir si un élément a le focus.

[3] d'après ce test

[4] hormis pour des applications dont la cible est Gecko, toutes les applications développées en XUL pour XulRunner ou Prism par exemple, ou les applications web pour FF ;-)