Les sélecteurs

En CSS, les sélecteurs sont utilisés afin de cibler une partie spécifique d'une page web à mettre en forme. Afin de pouvoir être précis, CSS est très riche en sélecteurs et une grande partie de sa flexibilité dépend de ceux-ci. Dans cet article, nous verrons coment ils fonctionnent et comment ils sont liés à votre code HTML.

Dans un article précédent, nous expliquions la syntaxe CSS et le lexique qui lui est lié. Les sélecteurs sont un des composant d'une règle CSS et sont placés avant les blocs de déclarations.

Les sélecteurs peuvent être classés en différentes catégories : les sélecteurs simples, les sélecteurs d'attributs, les pseudo-classes, les pseudo-éléments et les combinateurs. Nous allons détailler chacune de ces catégories.

Les sélecteurs simples

Ces sélecteurs sont qualifiés de simples car ils correspondent directement à un élément du document.

Les sélecteurs de type (type selectors)

Aussi appelés sélecteurs d'élément, ces sélecteurs correspondant aux éléments en ayant le même nom (quelle que soit la casse) que la balise HTML. C'est la méthode la plus simple pour cibler tous les éléments d'un type donné. Prenons un exemple :

Voici le code HTML que nous allons utiliser :

<p>Quelle couleur préfères-tu ?</p>
<div>J'aime le bleu.</div>
<p>Je préfère le rouge !</p>

La feuille de style avec des règles simples :

/* Tous les éléments p seront rouge */
p {
  color: red;
}

/* Tous les éléments div seront bleus */
div {
  color: blue;
}

On obtiendra alors le résultat suivant :

Les sélecteurs de classe

Un sélecteur de classe est composé d'un point (.), suivi d'un nom de classe. Un nom de classe correspond à n'importe quelle valeur (sans espace) qui peut être mise dans un attribut HTML class. Vous pouvez choisir ce que bon vous semble pour le nom de la classe. Précisons également que plusieurs éléments d'un document peuvent avoir la même classe et qu'un seul élément peut avoir plusieurs classes, chacune séparée par un espace. Voici un exemple :

Le code HTML :

<ul>
  <li class="premier fini">Créer un document HTML</li>
  <li class="deuxieme fini">Créer une feuille de style CSS</li>
  <li class="troisieme">Lier l'ensemble</li>
</ul>

La feuille de style :

/* L'élément avec la classe "premier" est mis en gras */
.premier {
  font-weight: bold;
}

/* Tous les éléments avec la classe "fini" sont barrés */
.fini {
  text-decoration: line-through;
}

Ces deux ingrédients nous permettent d'obtenir le résultat suivant :

Les sélecteurs d'identifiant

Un sélecteur d'identifiant commence par un dièse (d'aucuns diront croisillon) (#), suivi par le nom de l'identifiant attribué à un élément. Tout élément ne peut avoir qu'un seul nom d'identifiant, unique, grâce à l'attribut id. Là encore, vous pouvez choisir le nom que vous voulez pour l'identifiant. Cette méthode est la plus efficace pour sélectionner un seul élément.

Attention : Un nom d'identifiant doit être unique pour un document donné. On ne peut pas prédire le comportement qu'on obtiendra lorsqu'on utiliser plusieurs fois le même identifiant !

Voici un rapide exemple qui utilise :

Ce code HTML :

<p id="poli">Bonne journée.</p>
<p id="grossier">Va au diable !</p>

Et cette feuille de style simple :

#poli {
  font-family: cursive;
}

#grossier {
  font-family: monospace;
  text-transform: uppercase;
}

On obtiendra le résultat suivant :

Le sélecteur universel

Le sélecteur universel, représenté par *, est le plus large. Il permet de sélectionner tous les éléments d'une page. Il est rarement utile d'appliquer une même mise en forme sur toute une page. Ce sélecteur est donc généralement utilisé avec d'autres sélecteurs (voir les mécanismes de combinaison ci-après).

Attention : Lorsqu'on utilise le sélecteur universel, les règles correspondantes sont appliquées sur tous les éléments de la page. Pour les pages web plutôt grandes, cela peut augmenter le temps d'affichage de la page (et ainsi réduire de façon sensible les performances du site).

Voici un exemple qui utilise ce code HTML :

<div>
  J'aimerai être <strong>important</strong> ou
  <mark>marqué</mark> ou <em>mis en avant</em>, mais
  tout semble bien uniforme ici.
</div>

Voici la feuille de style correspondante :

* {
  font-weight: normal; /* on réinitialise font-weight par défaut pour <strong> */
  font-style : normal; /* on réinitialise font-style par défaut pour <em> */
  background : none;   /* on réinitialise background par défaut pour <mark> */
}

Cela donnera le résultat suivant :

Les sélecteurs d'attribut

Les sélecteurs d'attribut permettent de sélectionner les éléments HTML en fonction de leurs attributs et des valeurs de ceux-ci. Pour utiliser ces sélecteurs, on écrira des crochets "[]" dans lesquels on placer le nom de l'attribut et éventuellement une condition sur la valeur de l'attribut. Les sélecteurs d'attributs peuvent être classés en deux catégories : ceux qui filtrent sur la valeur de l'attribut et ceux qui filtrent uniquement sur la présence de l'attribut.

Définition et valeur des sélecteurs d'attribut

Les sélecteurs d'attribut suivants servent à associer une valeur exacte à un attribut précis :

  • [attr] : ce sélecteur sélectionnera tous les éléments avec l'attribut attr, quelque soit sa valeur.
  • [attr=val] : ce sélecteur sélectionnera tous les éléments avec l'attribut attr, mais seulement si la valeur est égale à val.
  • [attr~=val]: ce sélecteur sélectionnera tous les éléments avec l'attribut attr, seulement si la valeur val correspond à une des valeurs attr, séparées par des espaces.

Voici un exemple qui utilise ce code HTML :

Ingrédients pour la recette : <i lang="fr-FR">Poulet basquaise</i>
<ul>
  <li data-quantity="1kg" data-vegetable>Tomates</li>
  <li data-quantity="3" data-vegetable>Oignons</li>
  <li data-quantity="3" data-vegetable>Ail</li>
  <li data-quantity="700g" data-vegetable="not spicy like chili">Poivron rouge</li>
  <li data-quantity="2kg" data-meat>Poulet</li>
  <li data-quantity="optional 150g" data-meat>Lardons</li>
  <li data-quantity="optional 10ml" data-vegetable="liquid">Huile d'olive</li>
  <li data-quantity="25cl" data-vegetable="liquid">Vin blanc</li>
</ul>

Et la feuille de style correspondante :

/* Tous les éléments avec l'attribut "data-vegetable"
   sont en vert */
[data-vegetable] {
  color: green
}

/* Tous les éléments avec l'attribut "data-vegetable"
   et de valeur exacte "liquid" sont couleur dorée */
[data-vegetable=liquid] {
  color: goldenrod;
}

/* Tous les éléments avec l'attribut "data-vegetable",
   comportant la valeur "spicy" parmi d'autres
   sont en rouge */
[data-vegetable~=spicy] {
  color: red;
}

Et le résultat :

Les sélecteurs d'attribut utilisant un filtre sur les fragments de chaînes

Ces sélecteurs d'attribut sont également appelés sélecteurs d'expressions rationnelles car leur syntaxe et leur flexibilité se rapproche des expressions rationnelles. Cette approximation est légèrement exagérée car ces sélecteurs ne sont pas aussi riches que les expressions rationnelles :

  • [attr|=val] : Ce sélecteur sélectionnera tous les éléments dont l'attribut attr vaut val ou commence par val- (attention, le tiret fait bien partie du préfixe)
  • [attr^=val] : Ce sélecteur sélectionnera tous les éléments dont la valeur de l'attribut attr commence par val
  • [attr$=val] : Ce sélecteur sélectionnera tous les éléments dont la valeur de l'attribut attr finit avec val
  • [attr*=val] : Ce sélecteur sélectionnera tous les éléments dont la valeur de l'attribut attr contient la chaîne val (à la différence de [attr~=val], ce sélecteur ne considère pas que les espaces font partie de la valeur de l'attribut)

Poursuivons en complétant l'exemple précédent avec de nouvelles règles CSS :

/* On choisit ici la langue */
[lang|=fr] {
  font-weight: bold;
}

/* Tous les éléements dont l'attribut "data-vegetable"
   contient "not spicy" seront remis en vert
   (on notera les doubles quotes qui permettent de 
   montrer qu'on souhaite gérer les espaces) */
[data-vegetable*="not spicy"] {
  color: green;
}

/* Cela s'applique à tous les éléments dont la valeur
   de l'attribut "data-quantity" finit par "kg" */
[data-quantity$=kg] {
  font-weight: bold;
}

/* Cela s'applique à tous les éléments dont la valeur 
   de l'attribut "data-quantity" commence avec 
   "optional" */
[data-quantity^=optional] {
  opacity: 0.5;
}

Avec ces nouvelles règles, on obtient :

Les pseudo-classes

Une pseudo-classe CSS est un mot-clé précédé de deux points (:). Lorsqu'il est ajouté après un sélecteur, cela permet de choisir l'état de l'élément sur lequel appliquer la mise en forme. Par exemple, quand on utilise la pseudo-classe :hover, les règles s'appliqueront lorsque l'utilisateur survolera (avec sa souris, son doigt, son stylet, etc.) l'élément ciblé par le sélecteur.

Les pseudo-classes permettent d'appliquer une mise en forme sur un élément, non pas par rapport à l'arbre du document mais par rapport à des facteurs externes (par exemple, l'historique de navigation avec (:visited), le statut de son contenu avec :checked, la position du pointeur avec :hover).

Nous ne détaillerons pas ici chacune de ces pseudo-classes (à quoi bon vous gâcher le plaisir de la découverte). Pour le moment, prenons un exemple concret avec un fragment de HTML :

<a href="https://developer.mozilla.org/" target="_blank">Mozilla Developer Network</a>

Quelques règles CSS :

/* La mise en forme de base pour les liens */
a {
  color: blue;
  font-weight: bold;
  text-decoration: none;
}

/* On veut que les liens visités aient la 
   même couleur */
a:visited {
  color: blue;
}

/* Lorsqu'on survole le lien avec la souris,
   qu'on l'active avec le clavier, on le met
   en avant */
a:hover,
a:active,
a:focus {
  color: darkred;
  text-decoration: underline;
}

Voyons le résultat obtenu :

Les pseudo-éléments

Les pseudo-éléments ressemblent beaucoup aux pseudo-classes : ce sont des mots-clés précédés par deux deux-points (::) et qui sont ajoutés aux sélecteurs. Les pseudo-éléments permettent de représenter des parties d'éléments qui ne peuvent pas être isolées uniquement grâce au DOM, on peut voir cela comme un « emplacement » par rapport à l'élément sélectionné :

Chacun a un comportement spécifique et ce n'est pas le but de cet article que d'aller dans les détails de chacun. Prenons tout de même un exemple avec un fragment de HTML :

<ul>
  <li><a href="https://developer.mozilla.org/fr/docs/Glossaire/CSS">CSS</a> défini dans le glossaire MDN.</li>
  <li><a href="https://developer.mozilla.org/fr/docs/Glossaire/HTML">HTML</a> défini dans le glossaire MDN.</li>
</ul>

Et quelques règles CSS :

/* Tous les éléments dont la valeur de l'attribut "href"
   commence par "http" seront suivis d'une flèche
   pour indiquer un lien externe */
[href^=http]::after {
  content: '⤴';
}

Cela donnera le résultat suivant :

Les combinateurs

S'il n'était possible que d'utiliser un seul sélecteur à la fois, CSS ne serait pas très efficace. CSS devient particulièrement puissant lorsqu'on combine les sélecteurs pour obtenir un résultat précis. Selon les relations entre les éléments, CSS permet de combiner les sélections. Ces relations sont exprimées sous la forme « combinateurs » (combinators). Dans le tableau qui suit, A et B représentent n'importe quel sélecteur que nous avons vus précédemment :

Combinateur Élément(s) sélectionné(s)
AB Tout élément qui correspond à A et à B
A B Tout élément qui correspond à B et qui est un descendant d'un élément correspondant à A (c'est-à-dire que l'élément correspondant à B sera un fils (voire un fils d'un fils, voire un fils d'un fils d'un fils...) d'un élément correspondant à A.
A > B Tout élément qui correspond à B et qui est un fils direct d'un élément correspondant à A
A + B Tout élément qui correspond à B et qui est le prochain voisin d'un élément correspondant à A (c'est-à-dire le prochain fils du même parent)
A ~ B Tout élément correspondant à B et qui est un voisin d'un élément correspondant à A (c'est-à-dire un des fils du même parent)

Illustrons ce concept avec un exemple :

<table lang="en-US" class="with-currency">
  <thead>
    <tr>
      <th scope="col">Produit</th>
      <th scope="col">Qté</th>
      <th scope="col">Prix</th>
    </tr>
  </thead>
  <tfoot>
    <tr>
      <th colspan="2" scope="row">Total :</th>
      <td>148.55</td>
    </tr>
  </tfoot>
  <tbody>
    <tr>
      <td>Chaise longue</td>
      <td>1</td>
      <td>137.00</td>
    </tr>
    <tr>
      <td>Marshmallow</td>
      <td>2</td>
      <td>1.10</td>
    </tr>
    <tr>
      <td>Livre</td>
      <td>1</td>
      <td>10.45</td>
    </tr>
  </tbody>
</table>

On utilisera la feuille de style suivante :

/* La mise en forme simple pour le tableau */
table {
  font: 1em sans-serif;
  border-collapse: collapse;
  border-spacing: 0;
}

/* tous les <td> au sein d'une <table> et tous les <th> d'un <table>
   La virgule n'est pas un combinateur, elle permet juste
   d'avoir plusieurs sélecteurs qui s'appliquent pour le même ensemble
   de règles */
table td, table th {
  border : 1px solid black;
  padding: 0.5em 0.5em 0.4em;
}

/* tous les <th> d'un <thead> dans une <table> */
table thead th {
  color: white;
  background: black;
}

/* tous les <td> qui sont précédés par un autre <td>,
   au sein d'un <tbody>, au sein d'une <table> */
table tbody td + td {
  text-align: center;
}

/* Tous les <td> qui sont les derniers fils d'un 
   <tbody> au sein d'une <table> */
table tbody td:last-child {
  text-align: right
}

/* tous les <th> au sein d'un <tfoot> au sein d'une <table> */
table tfoot th {
  text-align: right;
  border-top-width: 5px;
  border-left: none;
  border-bottom: none;
}

/* tous les <td> précédés d'un <th> dans une <table> */
table th + td {
  text-align: right;
  border-top-width: 5px;
  color: white;
  background: black;
}

/* tous les pseudo-éléments "before" des <td> qui sont les derniers fils
   au sein d'un élément de classe "with-currency" et dont l'attribut 
   "lang" vaut "en-US" */
.with-currency[lang="en-US"] td:last-child::before {
  content: '$';
}

/* tous les pseudo-éléments "after" des <td>s qui sont les derniers fils
   au sein d'un élément de classe "with-currency" et dont l'attribut
   "lang" vaut "fr" ou commence par "fr-" */
.with-currency[lang|="fr"] td:last-child::after {
  content: ' €';
}

On obtiendra alors :

La suite

Les sélecteurs sont très riches et nous n'avons vu ici que la partie émergée de l'iceberg. Même les développeurs web les plus avancés continuent de découvrir le potentiel des sélecteurs. Cet article nous a permis d'explorer les concepts de base sur les sélecteurs. Lorsque vous les manipulerez, vous verrez parfois que le résultat obtenu n'est pas celui auquel vous vous attendiez et vous apprendrez que l'ordre des sélecteurs est crucial. Ce sujet est l'objet de notre prochain article : la cascade et l'héritage.

Étiquettes et contributeurs liés au document

Étiquettes : 
 Contributeurs à cette page : Banban, SphinxKnight, PetiPandaRou
 Dernière mise à jour par : Banban,