Sélectionner les frères d'un élément en CSS

Articlecss

Publié par le , mis à jour le (131224 lectures)

créer un menu en css selection cibler frères nth-child

Également appelés les "combinateurs d'adjacence", ces sélecteurs avancés permettent de cibler des éléments proches d'un autre élément.
Il existe à l'heure actuelle deux sélecteurs de ce type : le combinateur d'adjacence directe, et le combinateur d'adjance indirecte.

Le sélecteur d'adjacence directe

Le sélecteur d’adjacence directe est compatible avec quasiment tous les navigateurs.

La prise en charge de ce sélecteur est intéressante, puisque Firefox 1+, Safari 1.3+, Opera 9.2+, Chrome 2.0+ et IE7+ comprennent ce sélecteur.
Petite nuance pour Safari qui a un problème d'interprétation lorsqu'on cible le frère d'un élément en hover  jusqu'à la version 5. De même pour IE7, ne laissez pas trainer un commentaire HTML entre les deux éléments sinon le sélecteur ne fonctionnera pas.

Vous pouvez donc vous lâcher et l'utiliser dans vos intégrations.

Syntaxe de base

Pour cibler le frère d'un élément, nous allons avoir besoin du symbole +.
Voici sa syntaxe :

element1 + element2 {
   /* mes styles */
}

Ici les styles seront appliqués uniquement à element2 directement frère de element1.

C'est un bon moyen de cibler un élément dans un cas particulier, comme un p situé juste après un titre, ou pour appliquer des styles à des éléments semblables, sauf le premier de la fratrie.

Quelques exemples d'utilisation

Nous allons reprendre les deux exemples cités précédemment.

Appliquer une marge après un titre

Imaginons que nous souhaitions appliquer une marge suffisante pour espacer un p d'un h2 qui le précède, tout en conservant  un certain rapprochement lorsque h2 est suivi d'un h3 (l'un servant de sous-titre direct à l'autre).
Voilà ce que nous pourrions imaginer :

h2 + p {
   margin-top: 0.8em
}
h2 + h3 {
   margin-top: 0.2em
}

Il faudrait prévoir avant cela une annulation des marges existantes pour les redéfinir, mais cette méthode, avec quelques adaptations, pourrait améliorer le confort de lecture d'un texte fortement hiérarchisé.

Voir l'exemple

Appliquer un style différent au premier élément d'une liste

Deuxième scénario : je veux créer un menu horizontal où chaque élément de liste serait séparé de son frère par une bordure.
Mais voilà, si je dis que tous mes li possèdent une bordure à gauche, je me retrouve avec une bordure indésirable à gauche du premier élément. Si je désigne une bordure à droite, idem mais à l'opposé !

La solution ? Appliquer une bordure à gauche de tous les éléments de liste, sauf le premier.

li + li {
   border-left: 1px solid #aaa;
}

Il est également possible d'appliquer un style à tous les liens de  ce menu, en dehors du premier, en procédant de la même manière.
Cela peut-être utile si vous préférez appliquer les bordures aux liens, au lieu de les appliquer aux li.

Voir l'exemple

li + li a {
   border-left: 1px solid #aaa;
}

Cette technique peut vous permettre de changer de couleur de bordure au survol ou au focus du lien. Ça n'est qu'un exemple.

Le sélecteur d'adjacence indirecte

Le sélecteur d’adjacence indirecte est peu connu en CSS.
Arrivé avec la nouvelle mouture CSS3, ce sélecteur vous permet d'appliquer des styles d’un seul coup à tous les frères d’un élément ciblé répondant à un sélecteur précis.

La prise en charge est plutôt large pour un sélecteur CSS3, puisque Firefox 1+, Safari 3.1+, Opera 9.2+, Chrome 2.0+ et IE7+ comprennent ce sélecteur.
Petite nuance cependant pour IE7 qui n'aime pas qu'un commentaire HTML vienne s'interposer entre des frères, et pour IE8 qui ne ciblera que les 298 premiers éléments... ça laisse de la marge quand même.

Avec la mort de IE6, vous pouvez déjà vous permettre de l'utiliser !
Voyons voir avec quelques exemples comment l’utiliser et quels sont ses effets.

Syntaxe de base

Le sélecteur, que j’appelle incorrectement siblings du fait de sa ressemblance avec la fonction siblings() de jQuery, utilise le caractère ~ disponible sous PC depuis les touches Alt Gr + 2(é ~) et sous Mac de puis les touches Alt + N.

element1 ~ element2 {
   /* mes styles */
}

Ici les styles seront appliqués à tous les element2 frères de element1, même si un autre frère vient se mêler entre deux element2, par exemple.
Voir l'exemple

Il est important de noter que ce sélecteur ne cible que les frères suivants d’un élément ciblé. En aucun cas les frères précédents ne seront concernés.

Quelques exemples d’utilisations

L'habitude d’utiliser d’autres techniques avec le combinateur d'adjacence directe m'a un peu bloqué dans la recherche d'exemples.
Cependant, voici quelques idées, probablement non des plus probantes, mais pouvant servir malgré tout.

Cibler les frères non identiques à l’élément de départ

Ce sélecteur d’adjacence indirecte (~) va permettre de combler certaines lacunes du sélecteur d’adjacence directe (+).
Il sera capable de cibler tous les paragraphes qui suivent un titre, par exemple. Chose qui ne fonctionnera pas en faisant h3 + p, mais qui fonctionnera avec h3 ~ p.

Cibler les frères d’un élément survolé, ciblé ou typé

Dans la même logique que précédemment, il est possible de cibler, par exemple, tous les frères d’un li survolé (donc les autres li), ou d’un input typé.

li:hover ~ li {
   opacity: 0.4;
}

Cet exemple provient d’un effet produit sur jq.creativejuiz.fr (avec une solution jQuery), ou sur la récente démonstration de Raphaël Goetter sur un fil d’ariane entièrement conçu en CSS : Breadcrumbs without images.

Ou avec le deuxième exemple :

[type="radio"] ~ input {
   margin-left: 2.6em;
}

Ce qui permettrait, dans un enchaînement de input + label de type radio, d’espacer les choix.

Voir l'exemple

Sélecteur d'adjacence directe négative

Cela n'existe pas... ou du moins, pour sélectionner les frères situés avant un élément, il faudra utiliser un sélecteur CSS3.
Ce sélecteur, ou plutôt cette pseudo-classe s'appelle nth-child().

Nth-child est compatible avec la plupart des navigateurs modernes. Il ne faudra cependant pas compter sur la prise en charge de cette pseudo-classe par IE version 8 et inférieures.
Firefox le comprend depuis sa version 3.5, Safari de puis sa 3.1, Opera depuis sa 9.5 et Chrome depuis la version 2.

Remarque : Ce qui suit est expérimental, cela devra faire l'objet de votre propre expertise, et ne pourra être utilisé que dans des cas et besoins précis.

Syntaxe de base

Une valeur doit être renseignée entre les parenthèses du sélecteur pour cibler précisément des éléments.
Nth-child() accepte comme valeur une expression numérique, ou les mots-clefs odd et even (pour cibler un élément sur deux).

:nth-child(3n+1) {
   /* mes styles */
}

L'expression numérique comprend sa position (n) qui sera utilisée pour le calcul (ici *3 + 1).
Ici, les éléments sélectionnés seront :

  • n vaut 0 : 3 x 0 + 1 = 1 (soit le 1er élément)
  • n vaut 1 : 3 x 1 + 1 = 4 (soit le 4ème élément)
  • n vaut 2 : 3 x 2 + 1 = 7 (soit le 7ème élément)

Voir l'exemple

Exemple

Revenons sur le but de l'utilisation de ce sélecteur : simuler un sélecteur d'adjacence négative.

Sélecteur d'adjacence directe négative.

Ce que l'ont veut ? Pouvoir cibler tous les éléments situés avant un élément précis.
En suivant la logique précédent, on peut voir que n s'incrémente. Il va donc nous falloir utiliser le symbole - (moins) pour le faire se décrémenter.

Nous aurons donc un sélecteur sous cette forme :

:nth-child(-n+3) {
   background: #f3f3f3;
}

Ici nth-child(-n+3) nous permet de cibler tous les frères précédent le 4ème élément.

Limites

Un exemple, plusieurs limites, en tout cas sur cette manière d'utiliser nth-child().

  • Cibler les frères d'un élément non identique ne semble pas possible;
  • Par extension, cibler les frères qui précédent un élément détenteur d'une classe ne semble pas possible ;
  • Idem pour les frères qui précédent un élément détenteur d'une pseudo-classe ;
  • pour cibler le premier et le dernier élément, préférez :first-child à :nth-child(-n+1) et :last-child à :nth-last-child(n+2), qui offrent de meilleures prises en charge par le panel des navigateurs.

Commentaires

Salut salut !

@Skoua : ah oui ! Et si tu regardes dans les ressources en fin d'article il y a une astuce donnée sur le blog de Raphaël qui imite le comportement d'un sélecteur CSS3 :)

@jmlapam : merci. J'essaye de proposer des choses expérimentées, quand c'est possible, mais il manque certainement des choses encore.