Depuis longtemps, on a souhaité pouvoir appliquer le style sur un élément en fonction de ses enfants.
Ceci est enfin possible depuis l’ajout du support de la pseudo-class :has() par tous les principaux navigateurs récents.
Le besoin initial
Pour illustrer le besoin, nous allons prendre l’exemple d’une carte qui contient du texte et potentiellement une image.
On souhaite effectuer un affichage différent de la carte si celle-ci possède une image ou non.
Lorsque le composant Card ne possède pas d’image :
Le titre prend toute la largeur et sa couleur est noire
le texte doit prendre toute la largeur
Lorsque le composant Card possède une image, la disposition doit changer :
l’image doit se mettre à gauche et ne prendre que la moitié de la largeur
le titre doit se mettre à droite, devenir orange et prendre que la moitié de la largeur
le texte doit prendre toute la largeur
on ajoute une ombre portée sur la Card
Jusqu’à présent, la seule solution que l’on avait était de créer une classe spécifique où on l’on anticipait le fait d’avoir une image.
Démo card with class modifier
My title card
Depuis longtemps, on a souhaité pouvoir appliquer le style sur un élément en fonction de ses enfants.
My title card with image
Depuis longtemps, on a souhaité pouvoir appliquer le style sur un élément en fonction de ses enfants.
<div class="cards"> <div class="card"> <h2>My title card</h2> <p>Depuis longtemps, on a souhaité pouvoir appliquer le style sur un élément en fonction de ses enfants.</p> </div> <div class="card card--with-image"> <img src="/images/avatar.webp" alt="avatar" width="80" height="80" /> <h2>My title card with image</h2> <p>Depuis longtemps, on a souhaité pouvoir appliquer le style sur un élément en fonction de ses enfants.</p> </div> </div>
.card { /* style de base */}.card--with-image { /* style de surcharge */}
Cela nous oblige à anticiper le fait d’avoir une image dans le DOM et à ajouter une classe sur notre élément HTML pour pouvoir appliquer notre CSS.
La solution avec :has()
Avec la nouvelle pseudo-classe :has(), il n’est désormais plus nécessaire d’ajouter une classe sur notre élément parent.
Voici ce que cela donnerait avec la nouvelle pseudo-classes :has() :
.card { /* style de base */}.card:has(img) { /* style de surcharge */}
Démo card with :has()
My title card
Depuis longtemps, on a souhaité pouvoir appliquer le style sur un élément en fonction de ses enfants.
My title card with image
Depuis longtemps, on a souhaité pouvoir appliquer le style sur un élément en fonction de ses enfants.
<div class="cards"> <div class="card"> <h2>My title card</h2> <p>Depuis longtemps, on a souhaité pouvoir appliquer le style sur un élément en fonction de ses enfants.</p> </div> <div class="card"> <img src="/images/avatar.webp" alt="avatar" width="80" height="80" /> <h2>My title card with image</h2> <p>Depuis longtemps, on a souhaité pouvoir appliquer le style sur un élément en fonction de ses enfants.</p> </div> </div>
C’est un peu le cas classique d’utilisation où l’on va juste vérifier la présence d’un élément enfant que soit le degré de parenté.
Spécifique layout
On remarque qu’il est tout à fait possible de modifier complètement le comportement et l’agencement des enfants d’un élément en fonction de leur présence ou non.
Par exemple avec les grid-template-areas :
On peut cumuler les :has() afin d’être plus strict sur la possession des différents sélecteurs d’enfants.
Cela restreint le ciblage car l’élément parent devra posséder tous les enfants.
/* Cible les éléments de class .card qui possèdent une image ET un h2 */.card:has(img):has(h2) { /* style */}
Le cumul souple du :has() (ET/OU LOGIQUE)
Si on souhaite cibler des éléments qui ont l’un des enfants ou tous les enfants
/* Cible les éléments de class .card qui possèdent une image ET/OU un h2 */.card:has(img, h2) { /* style */}
Sélection inverse du :has() (NON LOGIQUE)
Si on souhaite cibler des éléments qui ne possèdent pas un enfant, on peut combiner le :has() avec un :not() :
/* Cible les éléments de class .card qui ne possèdent pas une image */.card:not(:has(img)) { /* style */}
Sélection globale du :has()
Un autre cas d’usage classique est de pouvoir appliquer du style sur n’importe quel élément de la page en fonction de la présence ou non d’un autre élément dans la page.
Par exemple, on pourrait gérer un système de thèmes dark/light mode avec une checkbox dans le header du site.
Et si la case est cochée, on est en dark mode et inversement.
Lorsque je suis en dark mode, je souhaite appliquer un certain style au footer de page.
On peut également combiner le :has() avec le sélecteur combinateur ~, on va pouvoir cibler tous les éléments prédécesseurs à un autre élément même si celui-ci est éloigné.
Dans cet exemple, on a une série de labels avec un bouton radio pour enfant. Si un bouton radio est coché, alors on souhaite que tous les labels précédents aient une couleur jaune.