:has()
Wybiera element na podstawie tego, co zawiera lub co stoi za nim. Pozwala stylować rodzica według jego dzieci.
Przykład
#.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>Karta z obrazkiem ma dwie kolumny.</p></div>
</div>
<div class="card">
<div><strong>Bez obrazka</strong><p>Ta karta zostaje w jednej kolumnie.</p></div>
</div>
</div>Definicja i zastosowanie
#Pseudoklasa :has() sprawdza, czy wewnątrz elementu albo obok niego istnieje coś, co pasuje do podanego selektora. .card:has(img) wybiera karty, które zawierają obrazek, a form:has(:invalid) formularze z przynajmniej jednym błędnym polem. Przez lata taki „selektor rodzica” uchodził w CSS za niemożliwy.
Selektor w nawiasie jest względny wobec elementu. Bez kombinatora oznacza potomka na dowolnej głębokości, z > tylko bezpośrednie dziecko (ul:has(> li.active)), a z + lub ~ rodzeństwo stojące dalej: h2:has(+ p) to nagłówek, za którym stoi akapit. W ten sposób :has() daje też selektor poprzedniego sąsiada.
Warunek działa na żywo. label:has(input:checked) zmieni wygląd etykiety w chwili zaznaczenia pola, a body:has(dialog[open]) może zablokować przewijanie strony, gdy otwarte jest okno. Wiele zadań, które wymagały JavaScriptu, załatwisz jedną regułą.
Specyficzność liczy się jak w :is(): to waga najmocniejszego selektora z listy. :has() nie może zawierać kolejnego :has() ani pseudoelementów. Zawężaj go do konkretnych komponentów, bo bardzo szerokie reguły przeglądarka musi często przeliczać.
Składnia
#:has( <względna lista selektorów> )Przykładowe selektory
#- Karty, w których jest obrazek. Karta z samym tekstem zostaje szara.
- Zaznacz pole w trzeciej karcie: podświetla się cała karta, a nie tylko pole.
- Odwrócony warunek: karty bez obrazka.
- Kontener reaguje na stan pola leżącego w jego środku. Zaznacz pole i obserwuj ramkę.
Przykładowe selektory w praktyce
#.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);
}Więcej przykładów
#.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">Adres e-mail</label>
<input id="mail" type="email" placeholder="Wpisz coś bez znaku @">
<small class="error">To nie wygląda na adres e-mail.</small>
</div>Wpisz tekst bez znaku @. Ramka i komunikat zmieniają się na poziomie całego pola, choć błąd dotyczy tylko inputu.
Obsługa przeglądarek
#Szeroko dostępne · od 2023 roku
Działa we wszystkich nowoczesnych przeglądarkach, także na telefonach. Możesz używać bez obaw.
- Chrome
- Edge
- Firefox
- Safari
Dobre praktyki
#- Zawężaj
:has()do komponentu, np..card:has(img). Samotne:has(…)bez niczego przed dwukropkiem sprawdza każdy element strony. - Starsze przeglądarki pominą regułę z
:has()w całości. Zapewnij sensowny wygląd bez niej, a:has()traktuj jako ulepszenie, np. w bloku@supports selector(:has(a)). - Zanim dopiszesz rodzicowi klasę w JavaScripcie, sprawdź, czy nie wystarczy
:has()z pseudoklasą stanu, taką jak:checked,:focus-visibleczy:invalid.
Powiązane hasła
#- :is()Dopasowuje element do dowolnego selektora z listy. Skraca długie, powtarzalne selektory do jednego zapisu.
- :not()Wybiera elementy, które nie pasują do podanego selektora ani do żadnego z listy.
- :focus-withinPasuje do elementu, który sam ma fokus albo zawiera element z fokusem. Pozwala wyróżnić cały formularz lub kartę.
- :checkedPasuje do zaznaczonego pola wyboru, wybranego przycisku radio i wybranej opcji na liście select.
- :invalidPasuje do pól formularza, których wartość łamie reguły walidacji HTML, np. do pustego pola wymaganego albo błędnego adresu e-mail.
- A + BZnak + wybiera element B, który stoi bezpośrednio za elementem A i ma z nim wspólnego rodzica.
Widzisz błąd albo brakuje przykładu? Napisz do nas.