W przeszłości stosowanie różnych układów do komponentu na podstawie jego zawartości wymagało użycia JavaScriptu lub logiki warunkowej w języku szablonów HTML w celu wstrzykiwania klas modyfikatorów (takich jak .card--has-image lub .card--text-only).
Pseudoklasa :has() eliminuje tę potrzebę, działając jako selektor nadrzędny. Umożliwia używanie stylów warunkowych elementu kontenera na podstawie obecności lub braku określonych elementów podrzędnych.
Za pomocą :has() możesz łatwo definiować różne warianty układu całkowicie w CSS na podstawie rzeczywistej zawartości DOM komponentu. Możesz też opcjonalnie połączyć go z :not(), aby wyraźnie kierować na brak treści i definiować domyślne układy.
Implementowanie stylów kontenera na podstawie treści
OBOWIĄZKOWE: musisz użyć selektora :has() w elemencie kontenera, aby wykryć obecność określonych treści podrzędnych.
Aby utworzyć komponent, który zmienia układ w zależności od zawartości:
- Zdefiniuj domyślny styl: zastosuj style układu podstawowego do elementu kontenera (np. prostego stosu jednokolumnowego).
- Zastosuj zastąpienia oparte na treści: kieruj na kontener za pomocą
:has([child-selector])i zastosuj nowe style układu, gdy ta treść jest obecna (np. siatka wielokolumnowa).
Przykład: komponent karty, który przełącza się na układ obok siebie, jeśli jest obecny obraz.
/* 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>
Wskazówka dotycząca wydajności: gdy używasz :has(), ogranicz selektor do najbardziej szczegółowego kontenera komponentu (np. .article-card). Unikaj zakotwiczania go w elementach bardzo wysokiego poziomu, takich jak body:has(img), jeśli zmiany stylu są bardzo zlokalizowane, ponieważ szerokie zapytania :has() mogą powodować więcej ponownych obliczeń układu.
Strategie zastępcze
Jeśli styl układu oparty na treści ma kluczowe znaczenie dla wrażeń użytkownika lub projektu strony, musisz zapewnić rozwiązanie zastępcze dla przeglądarek, które nie obsługują selektora :has(). W przypadku efektów czysto dekoracyjnych elementu :has() można używać jako progresywnego ulepszenia bez kreacji zastępczej.
OBOWIĄZKOWE: podczas wdrażania kreacji zastępczej w przypadku układów krytycznych musisz użyć w kodzie CSS właściwości @supports not selector(:has(*)), aby zdefiniować tradycyjną kreację zastępczą opartą na klasach (np. .has-image).
W przeciwieństwie do interaktywnego określania stylu na podstawie stanu obecność treści jest zwykle znana w czasie renderowania. Najbardziej niezawodnym rozwiązaniem zastępczym jest wstrzyknięcie przez mechanizm szablonów po stronie serwera (lub generator witryn statycznych) klasy takiej jak .has-image do kontenera, jeśli element podrzędny (np. obraz) istnieje w danych.
Jeśli renderowanie po stronie serwera nie jest możliwe, musisz użyć małego skryptu z CSS.supports(), aby wykryć zawartość i dodać klasę podczas wczytywania lub po wstrzyknięciu zawartości dynamicznej.
/* 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');
}
});
}