:has() en CSS : enfin un sélecteur parent en CSS pur

:has() cible un élément selon ce qu'il contient, un sélecteur parent enfin possible en CSS pur, sans une ligne de JavaScript.
4 min de lecture
Believemy logo

Pendant des années, CSS a pu décrire un enfant à partir de son parent, mais jamais l'inverse : cibler un parent selon ce qu'il contient restait impossible sans JavaScript. La pseudo-classe :has() change complètement la donne.

C'est l'une des additions les plus attendues du langage, tant elle débloque des cas d'usage qui demandaient auparavant un script pour ajouter une classe manuellement selon le contenu d'un élément.


Définition

La pseudo-classe :has() cible un élément si au moins un des sélecteurs passés entre parenthèses correspond à quelque chose à l'intérieur de cet élément, ou parmi ce qui le suit selon le combinateur utilisé dans l'argument.


Le sélecteur parent enfin possible

CSS
/* une carte qui contient une image change de style */
.carte:has(img) {
  border: 2px solid #2d6cdf;
}

/* un formulaire qui contient un champ en erreur */
form:has(.champ-erreur) {
  background-color: #fff5f5;
}

/* un titre suivi directement d'un paragraphe */
h2:has(+ p) {
  margin-bottom: 8px;
}

Le premier exemple est le cas d'usage le plus parlant : une carte qui contient une image reçoit une bordure, alors qu'une carte sans image n'en reçoit aucune, sans qu'aucune classe n'ait été ajoutée manuellement pour signaler cette différence.


Combiner :has() avec des combinateurs

L'argument de :has() peut utiliser n'importe quel Combinateurs CSS, ce qui change radicalement ce qui est vérifié. .carte:has(img) vérifie la présence d'une image n'importe où à l'intérieur de la carte, tandis que .carte:has(> img) ne vérifie que les enfants directs, sans descendre plus profondément dans la structure.

SélecteurVérifie
.carte:has(img)Une image n'importe où à l'intérieur
.carte:has(> img)Une image en enfant direct uniquement
h2:has(+ p)Un paragraphe juste après le titre


Cas d'usage au-delà des exemples classiques

:has() sert aussi à styliser un label selon l'état de la case à cocher qu'il contient, à adapter la mise en page d'une grille selon le nombre d'éléments qu'elle contient, ou à réagir à un état de :hover sur un enfant pour modifier l'apparence d'un ancêtre, une relation que CSS ne savait tout simplement pas exprimer auparavant.

Bon à savoir:has() peut avoir un coût de calcul plus élevé qu'un sélecteur classique sur des pages très lourdes, car le navigateur doit évaluer le contenu de chaque candidat. Sur un site normal, cette différence reste négligeable, mais il vaut mieux éviter de l'utiliser dans une boucle générant des milliers d'éléments.


Adapter une mise en page selon son contenu

:has() permet aussi d'ajuster une mise en page entière selon ce qu'elle contient, un cas d'usage impossible auparavant sans script. Une grille qui ne contient qu'un seul élément peut par exemple recevoir une mise en page différente d'une grille qui en contient plusieurs, simplement en comptant les correspondances possibles dans l'argument de :has().

CSS
/* mise en page spéciale pour une grille à un seul élément */
.grille:has(> :only-child) {
  display: flex;
  justify-content: center;
}

Combiné avec :is() et :where() pour regrouper plusieurs conditions, :has() ouvre la voie à des feuilles de style qui réagissent intelligemment à la quantité de contenu réellement présente, un comportement qui demandait auparavant de compter les éléments côté serveur ou en JavaScript avant même le premier rendu de la page. Cette capacité change la façon de concevoir des composants réutilisables, puisqu'un même bloc CSS peut désormais s'adapter à un contenu variable sans condition écrite ailleurs que dans la feuille de style elle-même.


Questions fréquentes

Question:has() peut-il remplacer complètement JavaScript pour réagir au contenu ?

Pour du style pur, souvent oui. Mais :has() ne déclenche aucun événement et ne peut pas exécuter de logique, il reste limité à l'application conditionnelle de styles selon le contenu détecté.

QuestionPeut-on imbriquer un autre :has() à l'intérieur de :has() ?

Oui, la spécification l'autorise, mais un tel niveau d'imbrication rend rapidement le sélecteur difficile à comprendre et mérite d'être évité au profit d'une logique plus simple quand c'est possible.

Question:has() fonctionne-t-il sur tous les navigateurs récents ?

Oui, les navigateurs modernes le supportent tous désormais, mais il reste indispensable de vérifier la compatibilité exacte visée par un projet avant de s'appuyer dessus pour une fonctionnalité essentielle.

Termes connexes

Découvrez notre glossaire HTML et CSS

Parcourez les termes et définitions les plus couramment utilisés dans le domaine du développement HTML et CSS.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.