Des sélecteurs virtuels en CSS3

En combinant les sélecteurs CSS3 :nth-child et :nth-last-child, Matt Mastracci invente des sélecteurs virtuels et s'amuse à créer une galerie d'images redimensionnables qui fonctionne sans JavaScript. Très malin, complètement magique.

Par

Les sélecteurs CSS3 :nth-child et :nth-last-child sont puissants : non seulement ils peuvent remplacer :first-child et :last-child, mais ils permettent d’appliquer des schémas de styles plus complexes comme “les premiers trois enfants” ou “tous les enfants sauf les trois premiers”, “chaque quatrième enfant”, ou des combinaisons de schémas du genre “a*n+b”.

Mais saviez-vous que vous pouviez faire des choses plus intéressantes encore avec ces sélecteurs ? Par exemple, vous pouvez appliquer un style au troisième élément, mais seulement s’il est un enfant parmi cinq (le sélecteur virtuel :nth-of-m-child dont nous allons parler tout à l’heure). Ou styler tous les enfants d’un élément ayant m enfants (un autre sélecteur virtuel que nous appellerons :family-of-m).

Vous vous demandez peut-être à quoi nous pourrions appliquer tout cela... Le cas d’utilisation que j’ai en tête est une galerie d’images sans JavaScript qui redimensionne automatiquement les images et “qui marche, tout simplement”, indépendamment du nombre d’images qu’on ajoute à la galerie.

Voici un exemple de ce à quoi ça ressemble (cliquez ici pour voir la démo pleine page). Remarquez dans cette démo la façon dont les images sont dimensionnées automatiquement dans une grille sans qu’on ait besoin de JavaScript.


un chaton

:nth-of-m-child

Merci à xantys de HN qui m'a fait connaître l’existence de travaux antérieurs dans ce domaine, ici et .

Le premier sélecteur virtuel que nous allons construire s’appellera :nth-of-m-child. Il nous permettra de styler le n-ième enfant lorsqu’il est un des m enfants, et ce sera notre unité de base pour les travaux à venir.

Alors, comment obtenir ce sélecteur ? Facile : nous combinons :nth-child et :nth-last-child sur un élément unique qui permettra de sélectionner un élément à styler lorsque celui-ci sera à la position correcte, à la fois depuis le début et la fin de la liste des enfants. Par exemple, nous pouvons styler le troisième enfant si et seulement si il est un enfant parmi cinq :

span:nth-child(3):nth-last-child(3) { ... }

En détail et en bon français : c’est à la fois le troisième enfant et le troisième dernier enfant (c'est à dire le troisième en partant de la fin). Étant donnés n et m, la formule générale est :

:nth-child(n):nth-last-child(m+1-n)

En voici un exemple en action (cliquez ici pour une visualisation pleine page) :


image

:family-of-m

Maintenant que nous sommes capables de styler n-de-m, nous pouvons développer plus avant cette idée afin de styler tous les enfants lorsqu’il y en a m d’entre eux directement sous un noeud parent. Le secret ici est d’utiliser le sélecteur CSS3 ~ siblings non-adjacents qui va cibler les éléments de la fratrie. Par exemple, le sélecteur :

img ~ span { ... }

ciblera un <span> si et seulement si l’un de ses frères précédents était un élément <img>, quel que soit le nombre de frères qui les séparent. Nous allons combiner ce sélecteur et notre schéma :nth-of-m-child de la façon suivante :

span:nth-child(1):nth-last-child(5) ~ span { ... }

Ce schéma ciblera tous les frères adjacents au premier élément d'une famille de cinq, c’est à dire du deuxième au cinquième. Pour obtenir toute la rangée, il suffit d’utiliser une virgule pour cibler également le premier élément :

span:nth-child(1):nth-last-child(5), 
span:nth-child(1):nth-last-child(5) ~ span { ... }

En détail et en bon français : le premier enfant d‘une famille de cinq, tous les enfants adjacents à ce premier enfant.

En voici un exemple en action (cliquez ici pour une visualisation pleine page) :


image

Techniques avancées

Très bien, maintenant nous avons les outils en place pour styler nos images comme nous l’avons vu dans l’exemple.

Le premier regroupement de un à quatre est une simple application de la technique que nous venons de décrire. Lorsque nous arrivons à cinq, nous voulons appliquer un modèle différent, dans lequel la première ligne, ou les deux premières, affichent des images plus grandes, les images restantes étant affichées par triplets (voir l’exemple). Ce modèle devrait se répéter indépendamment du nombre d’images.

Voici l’exemple commenté. Remarquez bien que l’on pourrait certainement simplifier la chose en utilisant Flexbox, mais cet exemple a valeur d’exercice.

Commençons avec cinq, huit, onze et les autres représentants de ce modèle. Au lieu d’utiliser :nth-child(1):nth-last-child(...) comme précédemment, nous allons prendre :nth-child(1):nth-last-child(3n+5), qui correspondra au premier groupe de 5, 8, 11,...

/* les dimensions des 2 premières sont divisées par 2  (99% / 2) */
img:first-child:nth-last-child(3n+5) ~ img, img:first-child:nth-last-child(3n+5) {
    max-width: 49.5%;
    margin-right: 1%;
}

/* les dernières n - 2 sont divisées par 3 (98% / 3) */
img:first-child:nth-last-child(3n+5) + img ~ img {
    max-width: 32.6%;
    margin-right: 1%;
}

/* Mais la 2e, la 5e, la 8e... n'ont pas de marge à droite */
img:first-child:nth-last-child(3n+5) ~ img:nth-child(3n+2) {
    margin-right: 0;
}

C’est beaucoup plus simple pour six, neuf et douze :

/* Six, neuf, douze, ... sont tous à (98% / 3) */
img:first-child:nth-last-child(3n+6) ~ img, img:first-child:nth-last-child(3n+6) {
    max-width: 32.6%;
    margin-right: 1%;
}

/* Chaque 3e élément est sans marge à droite. */
img:first-child:nth-last-child(3n+6) ~ img:nth-child(3n) {
    margin-right: 0;
}

Sept, dix, treize, etc. sont similaires au modèle 5/8/11 :

/* Les 4 premiers sont à (99% / 2) */
img:first-child:nth-last-child(3n+7) ~ img, img:first-child:nth-last-child(3n+7) {
    max-width: 49.5%;
    margin-right: 1%;
}

/* Les derniers n - 4 sont à (98% / 3) */
img:first-child:nth-last-child(3n+7) + img + img + img ~ img {
    max-width: 32.6%;
    margin-right: 1%;
}

/* Le 2e, 4e, 7e, 10e,... n'ont pas de marge à droite */
img:first-child:nth-last-child(3n+7) + img, 
    img:first-child:nth-last-child(3n+7) ~ img:nth-child(3n+4) {
    margin-right: 0;
    outline: 1px solid red;
}

Conclusions, notes, futur

On peut faire des choses vraiment intéressantes avec :nth-child et le sélecteur ~ de siblings non-adjacents. Les techniques appliquées dans cet article fonctionneront également avec les sélecteurs de la même famille :nth-of-type et :nth-last-of-type.

Les performances navigateurs ne semblent pas être affectées par cette technique, quel que soit le terminal. Si vous l’utilisez sur une large échelle, il est clair que ce sera un paramètre à tester.

En combinant la technique avec Flexbox et avec flexwrap, on pourrait certainement encore simplifier notre exemple et il devrait être possible de traiter les images de manière plus élégante.

Vous pouvez aussi utiliser cette technique pour créer d'autres modèles intéressants, comme rechercher les correspondances uniquement lorsque le nombre d'enfants est pair ou impair, et autres formules intéressantes.

Je serais ravi de connaître vos idées ou vos propositions d'amélioration. Vous pouvez jouer avec ce JSFiddle. Des commentaires ? Suivez-moi sur Twitter @mmastrac et faites-le moi savoir.


Intéressé par CSS ? Sur la Cascade, retrouvez des articles et ressources.


Ressources complémentaires en anglais

Styling elements based on sibling count, par Lea Verou
Clever lists with CSS3 selectors, par André Luis

Ressources complémentaires en français

Le sélecteur :nth-child (Dictionnaire CSS de la Cascade, travail en cours)
Le sélecteur :nth-child, vidéo par Grafikart
Combinateurs et pseudo-classes, par Steven Bradley
Flexbox, guide complet, par Chris Coyier


original paru le dans Grack.com.

Sur l'auteur : est créateur d'entreprise, business angel, hacker (pas nécessairement dans cet ordre). On peut le suivre sur Twitter, Google+, Facebook, angel.co et sur son blog grack.com.

Traduit avec l’aimable autorisation de l’auteur.
Copyright Grack.com © 2015.