In het verleden had je JavaScript of voorwaardelijke logica in je HTML-templatetaal nodig om modifier-klassen (zoals .card--has-image of .card--text-only) te injecteren als je verschillende lay-outs wilde toepassen op een component op basis van de content.
De pseudoklasse :has() maakt dit overbodig door als bovenliggende kiezer te fungeren. Hiermee kun je een containerelement voorwaardelijk opmaken op basis van de aan- of afwezigheid van specifieke onderliggende elementen.
Met :has() kun je makkelijk verschillende indelingsvarianten definiëren in CSS op basis van de werkelijke DOM-content van een component. Je kunt deze ook combineren met :not() om expliciet de afwezigheid van content te targeten om standaardindelingen te definiëren.
Op content gebaseerde containerstijlen implementeren
VERPLICHT: Je moet de kiezer :has() gebruiken voor het containerelement om de aanwezigheid van specifieke onderliggende content te detecteren.
Zo maak je een component waarvan de indeling verandert op basis van de content:
- Definieer de standaardstijl: Pas de basisindelingsstijlen toe op het containerelement (bijvoorbeeld een eenvoudige stapel met één kolom).
- Contentgebaseerde overschrijvingen toepassen: Target de container met
:has([child-selector])en pas de nieuwe vormgevingsstijlen toe voor wanneer die content aanwezig is (bijvoorbeeld een raster met meerdere kolommen).
Voorbeeld: Een kaartcomponent die overschakelt naar een indeling naast elkaar als er een afbeelding aanwezig is.
/* 1. Define the default state on the component container */
/* This applies when there is NO image */
.article-card {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1.5rem;
border: 1px solid #ccc;
border-radius: 8px;
}
/* 2. Apply styles when a specific child element is present */
/* MANDATORY: Target the container and use :has() to check for the descendant */
.article-card:has(img) {
/* Change the layout to a row if an image exists */
flex-direction: row;
align-items: center;
}
/* Optional: Style the image itself (no :has() needed here) */
.article-card img {
width: 150px;
height: auto;
border-radius: 4px;
}
/* You can also combine :has() and :not() to explicitly target the ABSENCE of content */
/* This selects a card that DOES NOT have an image */
.article-card:not(:has(img)) {
/* E.g., apply a different background color for text-only cards */
background-color: #f9f9f9;
}
<!-- Assume an <h1> precedes these components in the document layout -->
<!-- A card WITH an image (will use row layout) -->
<article class="article-card">
<img src="thumbnail.jpg" alt="Article thumbnail" />
<div class="content">
<h2>Card With Image</h2>
<p>This card lays out its content horizontally.</p>
</div>
</article>
<!-- A card WITHOUT an image (will use default column layout) -->
<article class="article-card">
<div class="content">
<h2>Text-Only Card</h2>
<p>This card lays out its content vertically, and gets its background color from the :not(:has()) rule.</p>
</div>
</article>
Prestatietip: Als u :has() gebruikt, beperkt u de kiezer tot de meest specifieke componentcontainer (zoals .article-card). Veranker de kiezer niet aan elementen op een zeer hoog niveau, zoals body:has(img), als de stijlveranderingen zeer lokaal zijn, omdat brede :has()-query's meer herberekeningen van de indeling kunnen veroorzaken.
Reservestrategieën
Als de op content gebaseerde vormgeving van de lay-out essentieel is voor de gebruikerservaring of het paginaontwerp, moet je een fallback bieden voor browsers die de kiezer :has() niet ondersteunen. Voor puur decoratieve effecten kan :has() worden gebruikt als progressive enhancement zonder fallback.
VERPLICHT: Als je een fallback implementeert voor kritieke indelingen, moet je @supports not selector(:has(*)) gebruiken in je CSS om een traditionele op klassen gebaseerde fallback te definiëren (bijv. .has-image).
In tegenstelling tot interactieve op status gebaseerde opmaak is de aanwezigheid van content meestal bekend op het moment van weergave. De meest robuuste fallback is om je templating-engine aan de serverzijde (of generator voor statische sites) een klasse zoals .has-image in de container te laten injecteren als het onderliggende element (zoals een afbeelding) in de gegevens bestaat.
Als weergave aan de serverzijde geen optie is, moet u een klein script met CSS.supports() gebruiken om de content te detecteren en de klasse toe te voegen bij het laden of na de injectie van dynamische content.
/* Fallback CSS for older browsers */
/* We check if the browser DOES NOT support the :has() selector */
@supports not selector(:has(*)) {
/* Define a traditional modifier class that applies the exact same layout overrides */
.article-card.has-image {
flex-direction: row;
align-items: center;
}
.article-card:not(.has-image) {
background-color: #f9f9f9;
}
}
/* Fallback JavaScript for older browsers (if not using SSR to add the class) */
/* Check for support before running the script to avoid unnecessary work in modern browsers */
if (!CSS.supports('selector(:has(*))')) {
// Find all components that need checking
const cards = document.querySelectorAll('.article-card');
cards.forEach(card => {
// If the critical content exists, manually add the fallback class
if (card.querySelector('img')) {
card.classList.add('has-image');
}
});
}