Combinateurs et pseudo-classes CSS
Nous poursuivons notre introduction aux basiques de CSS avec un article de Steven Bradley sur les combinateurs et les pseudo-classes. La maîtrise de cette syntaxe vous aidera à mieux coder.
Les combinateurs
Comme leur nom le suggère, les combinateurs aident à combiner différents sélecteurs pour former de nouveaux sélecteurs, plus spécifiques.
Il existe quatre types de combinateurs ciblant les relations parent/enfants et frères existant entre les éléments.
Combinateur descendant E F
Ce premier combinateur est représenté par un espace entre deux éléments, par exemple ul / espace / li :
ul li
Ce combinateur cible un élément F qui est un descendant de l'élément E. Les combinateurs de descendants ne se limitent pas aux enfants, ils ciblent tous les descendants.
//HTML
<ul>
<li>List Item 1</li>
<li>List Item 2
<ol>
<li>List Item 2-1</li>
<li>List Item 2-2</li>
</ol>
</li>
<li>List Item 3</li>
</ul>
//CSS
ul li {background: red;}
Toutes les listes auront un background rouge, car chacune est un descendant (enfant ou petit-enfant) de la liste non-ordonnée.
Combinateur enfant E > F
Cible un élément F qui est l'enfant d'un élément E. À la différence du combinateur précédent, F doit être un enfant direct de E.
Avec le même HTML que ci-dessus et le CSS :
ul>li {background: red;}
seuls les List Item 1, 2 et 3 auront un background rouge, car ils sont les enfants de ul, alors que les items 2-1 et 2-2 sont ses petits-enfants.
Combinateur adjacent E + F
Cible un élément F immédiatement précédé par un élément E. Le mot adjacent est important : seul le premier élément après E sera ciblé.
//HTML
<h1>Heading</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
<p>Paragraph 3</p>
//CSS
h1+p {font-size: 1.5em;}
Seul le paragraphe 1 aura le style spécifié.
Combinateur général siblings E ~ F
Cible un élément F précédé par un élément E. À la différence du combinateur adjacent qui cible uniquement le premier frère, celui-ci cible tous les frères (siblings).
Avec le même HTML que ci-dessus et le CSS :
h1~p {font-size: 1.5em;}
tous les paragraphes auront le même style défini ci-dessus.
Au-delà des sélecteurs simples
Remarquez bien que vous n'êtes pas limités aux sélecteurs simples. Tous les sélecteurs, y compris les sélecteurs d'attributs, peuvent être employés d'un côté ou de l'autre du combinateur :
ul a[title]
li#first>a[rel="external"]
h1+p.intro
h2.myclass~p[class="intro"]
Les pseudo-classes
Il existe beaucoup de pseudo-classes, le W3C les a regroupées ainsi :
- pseudo-classes dynamiques
- pseudo-classes cibles
- pseudo-classes de langage
- pseudo-classes d'éléments UI (interface utilisateur)
- pseudo-classes de négation
- pseudo-classes structurelles
Vous en connaissez (et utilisez) certaines.
Pseudo-classes dynamiques
Ce groupe-ci, je suis certain que vous le connaissez déjà. Il s'agit des pseudo-classes relatives aux liens et aux actions utilisateurs.
Pseudo-classes de liens
E:link cible un élément E qui est la source d'un lien non encore visité.
E:visited> cible un élément E qui est la source d'un lien ayant été visité.
Pseudo-classes d'action utilisateur
Ciblent un élément E lors de certaines actions utilisateurs.
E:active lorsque le lien est actif (c.à.d lorsqu'on a cliqué dessus)
E:hover lorsque le visiteur passe la souris sur le lien
E:focus lorsque le lien a un focus actif
Je suppose que vous les connaissez suffisamment pour que je n'aie pas besoin d'approfondir. Une chose à garder en mémoire toutefois : lorsque vous utilisez :link, :visited, :hover et :active sur un même élément, rappelez-vous qu'ils doivent être listés dans cet ordre, et pour cela rappelez-vous LoVeHAte !
Pseudo-classe de cible
Si vous avez déjà créé une ancre en ajoutant un # à une url, alors vous avez déjà créé une pseudo-classe de cible dans votre html.
E:target cible un élément E qui est la cible (appelée ancre) de l'url à laquelle on se réfère.
//HTML
<a href="domaine.com/cette-page.html#un-endroit-dans-la-page">...</a>
<!-- et plus loin dans la page : -->
<span id="un-endroit-dans-la-page"></span>
//CSS
span:target {background: yellow;}
Le code ci-dessus place un background jaune derrière le span.
Un cas d'usage intéressant est lorsqu'on veut aider notre lecteur qui a cliqué sur un lien à trouver tout de suite le contenu (le mot, le paragraphe…) correspondant à ce lien.
Pseudo-classes de langage
E:lang(fr) cible un élément E dans le langage spécifié (ici "fr" = français)
//HTML
<body lang=fr>
<p>Je suis français.</p>
</body>
//CSS
p:lang(fr) {color: red;}
Si vous utilisez des langues multiples dans une page, vous avez la possibilité de cibler spécifiquement un texte dans telle ou telle langue.
Pseudo-classes d'UI
Pour améliorer l'usabilité des formulaires, on ajoute souvent des contraintes comme permettre ou interdire l'affichage de certains champs en fonction de ce que le visiteur a déjà rempli. Par défaut, les éléments html ne sont ni activés (enabled) ni désactivés (disabled). JavaScript sert typiquement à cela, mais on peut aussi le faire manuellement (quitte à les modifier dynamiquement ensuite).
E:enabled cible un élément d'interface utilisateur E qui est activé.
E:disabled cible un élément d'interface utilisateur E qui est désactivé.
E:checked cible un élément d'interface utilisateur E qui est "checké", comme un bouton-radio ou une checkbox.
//HTML
<form>
Preferred Contact:
<input type="radio" id="prefer" value="email" checked="checked" /> Email
<input type="radio" id="prefer" value="phone" /> Phone
Email: <input type="text" id="email" enabled="enabled" />
Phone: <input type="text" id="phone" disabled="disabled" />
</form>
//CSS
:enabled {color: green;}
:disabled {color: red;}
:checked {background: yellow;}
Ici, le rouge et le vert sont utilisés pour signaler aux visiteurs les éléments du formulaire disponibles. Les items checkés ont un background jaune, pour rendre plus visible leur statut de "déjà fait".
Il est probable que nous utiliserions Javascript pour changer les attributs enabled et disabled, selon la méthode choisie par l'utilisateur.
Pseudo-classe de négation
La pseudo-classe de négation fait exactement ce à quoi on peut s'attendre. Avec elle, on cible tout ce qui n'est pas le sélecteur nié.
E:not(S) cible un élément E qui n'est pas égal au sélecteur S spécifié entre parenthèses.
//HTML
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
//CSS
div:not(.two) {color: orange;}
Le CSS appliquera une couleur orange au texte des première et troisième div, qui n'ont pas la classe "two". C'est simple, mais ça peut devenir rapidement complexe, attention.
Vous trouverez certainement d'autres façons de cibler les éléments que vous voulez sans utiliser le sélection de négation, mais une négation peut remplacer beaucoup de sélecteurs à elle toute seule, elle peut donc être appropriée dans certains cas.
Les pseudo-classes structurelles
Les pseudo-classes structurelles ont été introduites afin de pouvoir cibler des éléments html à partir d'informations tirées du DOM qui ne peuvent être facilement représentées par de simples sélecteurs ou combinateurs.
Vous en utilisez peut-être déjà certaines. Elles vous éviteront souvent d'ajouter des id ou classes inutiles.
E:root cible un élément E qui est la racine du document. En html il s'agit toujours de l'élément html.
:root {background: blue;}
html {background: blue;}
Ces lignes de CSS sont fonctionnellement équivalentes. Ok, ce n'est peut-être pas la plus utile des pseudo-classes structurelles, en tout cas c'est la plus simple à expliquer !
E:nth-child(n) cible un élément E qui est le n-ième enfant de son parent.
Supposons que nous ayons une table comportant de nombreuses lignes.
tr:nth-child(4) {background: #ccc;}
Le CSS ci-dessus indique qu'il faut trouver la quatrième ligne et lui donner un background gris clair.
On utilise souvent la pseudo-classe nth-child pour colorer alternativement les lignes d'une table.
tr:nth-child(2n+1) {background: #ccc;}
tr:nth-child(2n) {background: #eee;}
2n+1 représente les lignes impaires et 2n les lignes paires. On peut aussi utiliser les valeurs impaire (odd) et paire (even).
tr:nth-child(odd) {background: #ccc;}
tr:nth-child(even) {background: #eee;}
Bien entendu vous n'est pas limité à 2 :
tr:nth-child(10n+1) {background: #ccc;}
Ici, les 11e, 21e, 31e, etc lignes auront un background gris clair.
E:nth-last-child(n) cible un élément E qui est le n-ième enfant de son parent, en comptant à partir du dernier enfant.
nth-last-child fonctionne comme nth-child, mais en commençant par le dernier élément de la liste. Reprenons notre exemple de table :
tr:nth-last-child(1) {background: #ccc;}
Ici, c'est la dernière ligne qui aura un background gris clair,
tr:nth-last-child(4) {background: #ccc;}
et ici c'est la 4ème ligne en partant du bas qui aura ce background.
On peut utiliser les même valeurs :
tr:nth-last-child(2n+1) {background: #ccc;}
tr:nth-last-child(2n) {background: #eee;}
tr:nth-last-child(odd) {background: #ccc;}
tr:nth-last-child(even) {background: #eee;}
Ces deux façons d'écrire ont le même résultat, des lignes paires et impaires avec un background différent. Il n'est pas sûr que nth-last-child soit la meilleure pseudo-classe pour cet effet, mais vous pouvez l'utiliser.
Une utilisation plus spécifique à nth-last-child est de cibler un ou plusieurs éléments à la fin d'une liste.
:nth-of-type et :nth-last-of-type
Les autres pseudo-classes structurelles sont des variations de ce qui précède, donc nous allons simplement voir leurs applications spécifiques.
E:nth-of-type(n) cible un élément E qui est le n-ième frère du même type.
E:nth-last-of-type(n) cible un élément E qui est le n-ième frère du même type, en partant du dernier.
nth-of-type et nth-last-of-type fonctionnent de la même façon que nth-child et nth-last-child, la différence étant qui nous ciblons un type d'élément spécifique et non pas n'importe quel enfant.
On utilise les pseudo-classes "of-type" lorsque le parent de l'élément qu'on cible a plusieurs types d'éléments enfants :
<div>
<p></p>
< img src="" alt="" / >
<p></p>
<ul>
<li></li>
<li></li>
</ul>
<p></p>
</div>
//CSS
p:nth-of-type(2) {font-size: 1.2em;}
p:nth-last-of-type(1) {font-size: 1.2em;}
Je suis sûr que vous voyez quels paragraphes sont ciblés par le CSS ci-dessus. Et la raison pour laquelle on utilise ici les "of-type" est que les éléments situés dans la div sont de types différents.
:first-child et :last-child
Ce sont en fait des raccourcis pour nth-child(1) et nth-last-child(1), et comme ces deux pseudo-classes, ils sous-entendent que les enfants sont de même type.
E:first-child cible un élément E qui est le premier enfant de son parent.
E:last-child cible un élément E qui est le dernier enfant de son parent.
On utilise souvent ces pseudo-classes dans les barres de navigation lorsqu'on veut donner un style au premier ou au dernier élément du menu pour les différencier des autres. Ou bien, il arrive fréquemment qu'on ajoute une bordure gauche ou droite aux éléments du menu pour les séparer, mais il faut du coup en ajouter (ou retirer) une d'un côté ou de l'autre. li:last-child ou li:first-child sont pratiques pour y arriver.
:first-of-type et :last-of-type
Ce sont les équivalents fonctionnels de nth-of-type(1) et nth-last-of-type(1). Ils fonctionnent de la même façon que first-child et last-child, sauf qu'ils ciblent des éléments d'un type particulier.
E:first-of-type cible un élément E qui est le premier frère de son type.
E:last-of-type cible un élément E qui est le dernier frère de son type.
Utilisation typique pour ces pseudo-classes : appliquer un style au premier paragraphe à l'intérieur d'une div, d'un article ou d'une section.
:only-child et :only-of-type
Ciblent des éléments qui n'ont qu'un enfant.
E:only-child cible un élément E qui est le seul enfant de son parent.
E:only-of-type cible un élément E qui est le seul de son type dans sa fratrie.
:empty
E:empty cible un élément E qui n'a pas d'enfants, ni de texte. C'est une façon de cibler les attributs vides. Par exemple, il ne ciblerait pas :
<p>Voici un texte</p>
mais il ciblerait :
<p></p>
Pour finir, si l'anglais n'est pas un problème voici une formidable vidéo de Chris Coyier présentant les pseudo-classes et pseudo-éléments.
Les pseudo-éléments
Parfois nous aimerions appliquer un style à des éléments mais nous ne pouvons le faire à partir d'informations tirée du DOM. Nous résolvons le problème en ajoutant un élément (un span par exemple) auquel nous donnons une classe. Dans certains cas, le recours aux pseudo-éléments s'avère plus simple.
Nous en avons quatre :
- :first-line
- :first-letter
- :before
- :after
E:first-line cible la première ligne formatée d'un élément E.
p:first-line {text-transform: uppercase;}
Le CSS ci-dessus change la première ligne de tous les paragraphes, et bien sûr la première ligne de texte est dynamique si votre mise en page est flexible.
E:first-letter cible la première lettre d'un élément E.
Une utilisation évidente serait la création de lettrines :
<p class="intro">This is the first paragraph</p>
//CSS
p.intro:first-letter {
font-size:4em;
font-weight:bold;
color: #f00;
float:left;
}
E:before et E:after ciblent un contenu généré avant ou après un élément E.
Une utilisation courante est l'ajout de guillemets stylisés avant et après une citation :
<blockquote></blockquote>
//CSS
blockquote::before {content: "\2018";}
blockquote::after {content: "\2019;;";}
Pour une approche détaillée de :before et :after, vous pouvez lire l'article de Louis Lazaris ici-même.