Il est souvent nécesasire d'appliquer des styles différants à des éléments partageant la même classe, selon qu'ils contiennent ou non certains éléments enfants spécifiques. Cette technique s'avère particulièrement utile pour mettre en évidence des contenus modifiés ou des états particuliers.
Prenons l'exemple suivant : deux éléments code-block, dont l'un contient un élément enfant avec la classe updated et l'autre non.
` Bloc contenant un élément enfant spécifique <span class="updated">élément modifié</span>`
` Bloc sans élément enfant spécifique`
/* Styliser les éléments sans descendant .updated en orange */
.code-block:not(:has(.updated)) {
color: orange;
}
/* Styliser les éléments avec un descendant .updated en vert */
.code-block:has(.updated) {
color: green;
}
Plusieurs approches permettent de réaliser ce comportement :
Approche 1 : Combinaison de :not() et :has() (navigatuers modernes)
Cette méthode exploite les sélecteurs relationnels récents pour détecter la présence d'éléments enfants :
/* Par défaut : style pour les éléments sans enfant modifié */
.code-block:not(:has(.updated)) {
background-color: #f0f0f0;
border-left: 3px solid orange;
}
/* Style pour les éléments contenant un enfant modifié */
.code-block:has(.updated) {
background-color: #e8f5e9;
border-left: 3px solid green;
}
Approche 2 : Sélecteur de voisin (compatibilité étendue)
En modifiant légèrement la structure HTML, on peut utiliser un indicateur visuel :
<div class="code-block">
contenu 1
<span class="updated">élément</span>
<span class="status-indicator"></span>
</div>
<div class="code-block">
contenu 2
</div>
/* Style par défaut */
.code-block {
color: orange;
}
/* Override grâce à l'indicateur */
.code-block:has(.status-indicator) {
color: green;
}
Approche 3 : Classes CSS conditionnelles
L'ajout manuel de classes offre un contrôle total sur le stylage :
` contenu 1 <span class="updated">élément</span>`
` contenu 2`
.code-block {
color: orange;
}
.code-block.has-updated {
color: green;
}
Approche 4 : Attributs de données
Les attributs data permettent une séparation claire entre structure et état :
` contenu 1 <span class="updated">élément</span>`
` contenu 2`
.code-block {
color: orange;
}
.code-block[data-status="modified"] {
color: green;
}
Approche 5 : Assistance JavaScript (détection dynamique)
Pour les cas nécessitant une détection côté client :
document.querySelectorAll('.code-block').forEach(block => {
const hasUpdatedChild = block.querySelector('.updated') !== null;
block.classList.toggle('contains-update', hasUpdatedChild);
});
.code-block {
color: orange;
}
.code-block.contains-update {
color: green;
}
Recommandations
Pour les projets ciblant les navigateurs récents, l'approche 1 offre la solution la plus élégante et maintenable. Pour une compatibilité maximale ou lorsque la modification du HTML est acceptable, les approche 3 ou approche 4 constituent des alternatives robustes.
Points importants
- Le sélecteur
:has()est supporté depuis Chrome 105, Safari 15.4 et Firefox 121 - La spécificité des règles doit être vérifiée pour éviter les conflits de stylage
- Pour hériter les couleurs vers les enfants, s'assurer qu'aucune règle intermédiaire ne surchargera le comportement souhaité