:has()
Sélectionne un élément en fonction de ce qu’il contient ou de ce qui se trouve après lui. Permet de styliser un parent selon ses enfants.
Exemple
#.card {
display: grid;
gap: 10px;
}
.card:has(img) {
grid-template-columns: 56px 1fr;
align-items: center;
}<style>
.card { padding: 12px; border-radius: 12px; background: #18181b; }
.card img { width: 56px; height: 56px; border-radius: 12px; }
.card p { margin: 0; color: #a1a1aa; font-size: 14px; }
</style>
<div style="display: grid; gap: 10px;">
<div class="card">
<img src="/collections/astro.png" alt="Logo Astro">
<div><strong>Astro</strong><p>La carte avec une image a deux colonnes.</p></div>
</div>
<div class="card">
<div><strong>Sans image</strong><p>Cette carte reste sur une seule colonne.</p></div>
</div>
</div>Définition et utilisation
#La pseudo-classe :has() vérifie si quelque chose correspondant au sélecteur donné existe à l’intérieur de l’élément ou à côté de lui. .card:has(img) sélectionne les cartes qui contiennent une image, et form:has(:invalid) les formulaires avec au moins un champ incorrect. Pendant des années, ce « sélecteur de parent » passait pour impossible en CSS.
Le sélecteur entre parenthèses est relatif à l’élément. Sans combinateur, il désigne un descendant à n’importe quelle profondeur ; avec >, seulement un enfant direct (ul:has(> li.active)) ; et avec + ou ~, un frère placé plus loin : h2:has(+ p) désigne un titre suivi d’un paragraphe. :has() offre ainsi aussi un sélecteur de frère précédent.
La condition s’applique en direct. label:has(input:checked) changera l’apparence de l’étiquette au moment où le champ est coché, et body:has(dialog[open]) peut bloquer le défilement de la page quand une fenêtre est ouverte. De nombreuses tâches qui nécessitaient du JavaScript se règlent en une seule règle.
La spécificité se calcule comme dans :is() : c’est le poids du sélecteur le plus fort de la liste. :has() ne peut contenir ni un autre :has() ni de pseudo-éléments. Restreignez-le à des composants précis, car le navigateur doit souvent recalculer des règles trop larges.
Syntaxe
#:has( <liste de sélecteurs relative> )Exemples de sélecteurs
#- Les cartes qui contiennent une image. La carte avec seulement du texte reste grise.
- Cochez la case de la troisième carte : toute la carte se met en valeur, pas seulement la case.
- Condition inversée : les cartes sans image.
- Le conteneur réagit à l’état d’un champ situé à l’intérieur. Cochez la case et observez la bordure.
Exemples de sélecteurs en pratique
#.card:has(img)
.card:has(img) { background: #7c3aed; color: #fff; }.card:has(input:checked)
.card:has(input:checked) { background: #7c3aed; color: #fff; }.card:not(:has(img))
.card:not(:has(img)) { background: #7c3aed; color: #fff; }.demo:has(input:checked)
.demo:has(input:checked) {
border-color: #a78bfa;
background: rgb(124 58 237 / 0.15);
}Plus d’exemples
#.field:has(input:invalid) {
border-color: #f43f5e;
}
.field:has(input:invalid) .error {
display: block;
}<style>
.field { display: grid; gap: 6px; padding: 12px; border: 1px solid #3f3f46; border-radius: 12px; }
.field input { padding: 8px 10px; border: 1px solid #52525b; border-radius: 8px; background: #18181b; color: inherit; font: inherit; }
.error { display: none; color: #fda4af; font-size: 13px; }
</style>
<div class="field">
<label for="mail">Adresse e-mail</label>
<input id="mail" type="email" placeholder="Saisissez quelque chose sans le signe @">
<small class="error">Cela ne ressemble pas à une adresse e-mail.</small>
</div>Saisissez un texte sans le signe @. La bordure et le message changent au niveau de tout le champ, bien que l’erreur ne concerne que le champ de saisie.
Compatibilité des navigateurs
#Largement disponible · depuis 2023
Fonctionne dans tous les navigateurs modernes, y compris sur mobile. Vous pouvez l’utiliser sans crainte.
- Chrome
- Edge
- Firefox
- Safari
Bonnes pratiques
#- Restreignez
:has()à un composant, par exemple.card:has(img). Un:has(…)isolé, sans rien avant les deux-points, vérifie chaque élément de la page. - Les navigateurs plus anciens ignoreront entièrement une règle contenant
:has(). Prévoyez une apparence correcte sans elle, et traitez:has()comme une amélioration, par exemple dans un bloc@supports selector(:has(a)). - Avant d’ajouter une classe au parent en JavaScript, vérifiez si
:has()associé à une pseudo-classe d’état, comme:checked,:focus-visibleou:invalid, ne suffit pas déjà.
Entrées associées
#- :is()Fait correspondre l’élément à n’importe quel sélecteur de la liste. Raccourcit les longs sélecteurs répétitifs en une seule écriture.
- :not()Sélectionne les éléments qui ne correspondent à aucun des sélecteurs fournis, ni à aucun de la liste.
- :focus-withinCorrespond à un élément qui a lui-même le focus ou qui contient un élément avec le focus. Permet de mettre en valeur tout un formulaire ou une carte.
- :checkedCorrespond à une case cochée, à un bouton radio sélectionné et à l’option choisie dans une liste select.
- :invalidCorrespond aux champs de formulaire dont la valeur enfreint les règles de validation HTML, par exemple un champ requis vide ou une adresse e-mail incorrecte.
- A + BLe signe + sélectionne l’élément B qui se trouve immédiatement après l’élément A et partage le même parent que lui.
Vous voyez une erreur ou il manque un exemple ? Écrivez-nous.