Spis treści
- Wprowadzenie
- Pseudoklasa :has
- Użycie pseudoklasy :has do stylowania formularzy na bazie walidacji pól
- Podsumowanie
Wprowadzenie
Stack Overflow pełen jest pytań o to, czy w CSS istnieje „parent selector”. O ile możemy stylować dzieci bazując na tym, kto jest rodzicem – w drugą stronę póki co to nie działa.
Na szczęście w specyfikacji znajduje się pseudoklasa :has – a dzięki sprytnej bibliotece możemy używać jej nawet dziś, gdy jeszcze żadna przeglądarka go nie wspiera.
Zapraszam do przeczytania wpisu o tym, jak za pomocą tego selektora stylować walidację formularzy.
Pseudoklasa :has
Jak działa pseudoklasa :has?
Pseudoklasa :has wybiera element zawierający określona zawartość. Przykładowo:
.class:has(.another-class)
Wybierze element z klasą .class tylko wtedy, gdzie wewnątrz elementu .class znajduje się element z klasą .another-class.
Struktura ta mogłaby wyglądać tak:
<div class="class">
<p>
<h2>
Header
</h2>
<div class="another-class">
Some content
</div>
</p>
</div>
Będzie więc mega użyteczny do wszelkiej maści stylowania bazującego na walidacji – gdzie np jeśli mamy element w stanie :valid cała grupa elementów jak opis, label itd ma być zielona.
Jakie pseudoklasa :has ma wsparcie i jaką biblioteką go zasymulować?
W momencie pisania tekstu pseudoklasa nie ma żadnego wsparcia.
Na szczęście do momentu implementacji wspomóc nas może CSS Has Pseudo, plugin do PostCSS i biblioteka w jednym.
Umożliwia ona zamianę pseudoklasy :has na odpowiadający jej selektor atrybutu i wygenerowanie pliku zawierającego te zmiany. Następnie, już w kodzie strony, wywołanie metody cssHasPseudo spowoduje, że to elementów które w CSS mają selektor atrybutu zostanie ten atrybut dodany.
Użycie pseudoklasy :has do stylowania formularzy na bazie walidacji pól
Stworzenie skryptu transformującego CSS
Aby pseudoklasa została przetransformowana do atrybutu, musimy utworzyć odpowiedni skrypt. Skrypt ten zawierać będzie uruchomienie PostCSS wraz z pluginem has-pseudo i zapisanie zmian do pliku:
const postcss = require('postcss');
const postcssHasPseudo = require('css-has-pseudo/postcss');
const fs = require('fs')
fs.readFile('index-to-transform.css', (err, css) => {
postcss([postcssHasPseudo()])
.process(css, {
from: 'index-to-transform.css',
to: 'index.css' })
.then(result => {
fs.writeFile(
'index.css',
result.css,
() => true)
});
});
Style odpowiadające za kolorystykę walidacji
Nasze style opierać się będą bardzo mocno o CSS Variables – jeśli jeszcze ich nie znasz możesz poczytać o nich tutaj:
Stwórzmy bazowe style, zawierający kolory pod walidację formularza:
html {
--color-regular: black;
--color-error: red;
--color-success: green;
--validation-color: var(--color-regular);
}
Bazowym elementem do stylowania będzie klasa .form-group, na której ustawiać będziemy kolor walidacji, z którego korzystać będzie kod kolorujący poszczególne elementy:
.form-group {
width: 200px;
padding: 8px;
border: 1px solid var(--validation-color);
color: var(--validation-color);
}
Podobnie same pola formularza korzystać będą ze zmiennej --validation-color:
input {
border: 1px solid var(--validation-color);
color: inherit;
}
I w tym miejscu zaczyna się magia CSS Variables 🙂 Ponieważ zamiast zmieniać kolorystykę wszystkich elementów w reakcji na wynik walidacji, potrzebujemy zmienić tylko wartość jednej zmiennej:
.form-group:has(:invalid) {
--validation-color: var(--color-error);
}
.form-group:has(:valid) {
--validation-color: var(--color-success);
}
.form-group.pristine {
--validation-color: var(--color-regular);
}
Po co potrzebujemy dodatkowej klasy .pristine? Ponieważ CSS nie pozwala nam napisać selektora tak, by obejmował tylko elementy, w których użytkownik już coś wpisał. Domyślnie więc wszystkie pola są w statusie invalid, jeśli są wymagane.
Wymagany kod JS
Kod JS będzie bardzo prosty – najistotniejszym elementem jest zawołanie funkcji bibliotecznej, która stworzy atrybuty na bazie pseudoklas:
cssHasPseudo(document);
W kodzie strony łatwo zauważymy efekt jej wywołania:

Potrzebować jeszcze będziemy obsługi formularza – usunięcia klasy .pristine gdy użytkownik wpisuje coś w pole oraz przywrócenia jej, gdy formularz jest czyszczony po submicie:
form
.addEventListener('change', e => {
e.target
.closest('.form-group')
.classList
.remove('pristine');
});
form
.addEventListener('submit', e => {
e.preventDefault();
form.reset();
[...document.querySelectorAll('.form-group')]
.forEach(el => {
el.classList.add('pristine');
});
});
Efekt działania aplikacji
Efektem tak prostego kodu i sprytnego wykorzystania pseudoklasy i CSS Variables jest łatwe w implementacji prezentowanie użytkownikowi stanu walidacji formularza:

Klikalny przykład na Github Pages
Klikalny przykład znajduje się pod adresem:
https://radek-anuszewski.github.io/has-pseudoclass-demo/
Pełny kod przykładu na Github
Pełny kod aplikacji znajduje się pod adresem:
https://github.com/radek-anuszewski/has-pseudoclass-demo
Podsumowanie
Choć pseudoklasa :has nie jest jeszcze oficjalnie nigdzie wspierana łatwo zobaczyć, że znajdzie się dla niej wiele przypadków użycia. Połączona z nowoczesnymi funkcjonalnościami CSS jak CSS Variables uprości wiele sytuacji, które bez tego wymagałyby osobnych bibliotek czy też dodatkowego kodu JS.
Dlatego warto trzymać rękę na pulsie w sprawie tego, jaka jest przyszłość tej pseudoklasy.
