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.