Mało kto zdaje sobie sprawę z istnienia selektorów w CSS-ie. Jeszcze mniej osób potrafi je poprawnie wykorzystać. Po przeczytaniu tego krótkiego artykułu nauczysz się używać selektorów, zwiększając efektywność pisania własnego kodu.
Jak wiadomo, CSS wykorzystuje model {link_wew 194}DOM{/link_wew} (Document Object Model), a co za tym idzie obsługuje dziedziczenie. Obiekty zawierające coś wewnątrz siebie nazywa się często „przodkami”, a obiekty znajdujące się wewnątrz innych – „potomkami”.
Dla przykładu mamy dwie klasy: A i B. Element klasy B zawiera się w A (rys. 1.), ale istnieją też „luźne” elementy klasy B. Możemy więc bardzo łatwo wpływać na wygląd tylko tych elementów zawartych jednocześnie w A i B. Nie będzie zatem odkrywczym następujący kod:
.a {background-color:#ff8;}
.a .b {border: solid 2px #000;}
Taki zapis spowoduje, że tylko obiekty klasy B znajdujące się wewnątrz obiektów klasy A będą miały zarówno czarną ramkę. Pozostając w zgodzie z przyjętym nazewnictwem, możemy powiedzieć, że element A jest przodkiem dla elementów B. Jeden z obiektów B jest jednocześnie potomkiem A i przodkiem innych elementów B. Dodatkowo, bezpośredni przodek nazywany jest „rodzicem”, a bezpośredni potomek, analogicznie, „dzieckiem”.
Obiektowość w praktyce
Załóżmy jednak, że chcemy obramować na czerwono tylko obiekty znajdujące się bezpośrednio w obiektach klasy A – te zagłębione chcemy pozostawić w spokoju. Wydaje się to trudne? Może warto utworzyć jeszcze jedną klasę i żmudnie dopisywać kolejne słowa w kodzie warstwy informacyjnej? Nic takiego!
Dzięki selektorom dziedziczącym (child selectors) możemy wpływać tylko na obiekty będące dziećmi (bezpośrednimi potomkami) obiektu wybranej klasy. Przeanalizujmy kod:
.a>.b {background-color: #c00;}
Zamiast spacji, między klasami pojawił się nawias trójkątny. Jeśli poprzedni zapis oznaczał „wszystkie obiekty klasy B, znajdujące się również w klasie A”, to zastąpienie spacji nawiasem znacząco zmienia znaczenie kodu. W tym przypadku otrzymujemy „wszystkie obiekty klasy B, których rodzicem jest obiekt klasy A”.
W przykładzie różnica jest bardzo dobrze widoczna – czerwone tło mają tylko bezpośredni przodkowie klasy A. Oczywiście rozwiązanie to można wykorzystać na dużo szerszą skalę, a wyrażenia znajdujące się po lewej i prawej stronie nawiasu nie muszą być klasami. Równie dobrze mogą to być nazwy tagów czy id obiektów, wymieszane na różne sposoby. Rozwiązanie to sprawdza się bardzo dobrze, np. przy nadawaniu stylów listom.
Selektory sąsiadujące
Inną sztuczką jest wykorzystanie selektorów sąsiadujących (adjacent sibling selectors). Ich zastosowanie jest bardzo naturalne wszędzie tam, gdzie chcemy dekorować elementy znajdujące się w sąsiedztwie innych, określonych obiektów.
Przykładowo, jeśli chcielibyśmy (na wzór gazet) pogrubić tekst pierwszego akapitu pod tytułem możemy zrobić to na dwa sposoby. Pierwszym z nich jest ręczne nadanie wybranemu akapitowi danej klasy, np. „wstep”. Rozwiązanie takie wydaje się naturalne, jednak jeśli działamy na istniejących już danych, zmiana wszystkich tekstów może okazać niemożliwa. Zamiast tego wykorzystamy właśnie selektor sąsiadujący, oznaczony plusem.
p {font-weight: 300}
h2+p {font-weight: 700}
h2+p oznacza „akapity, które znajdują się bezpośrednio pod nagłówkiem”. Dzięki temu prostemu fragmentowi kodu nie musimy dodatkowo opisywać treści, co nie tylko zmniejsza łączną ilość kodu (jeśli rozpatrujemy co najmniej kilkuelementowy zbiór tekstów), ale również zapobiega ewentualnym pomyłkom lub zapomnieniu o nadaniu klasy „wstep” przez piszącego. Oczywiście oznaczenie nagłówka h2 jest przykładowe i równie dobrze można tam wpisać nazwę obiektu, klasy czy tagu.
Przykład z życia
Posłużmy się bardziej życiowym przykładem, aby połączyć użycie omówionych selektorów i zobaczyć, jak skuteczne okazują się w praktyce. Mamy do wykonania menu takie jak na rysunku 3.
Szare ramki dodano tymczasowo w celu pokazania powiązań między obiektami. Zbudowane zostało ono z wykorzystaniem zagnieżdżonych list, tak jak pokazuje fragment kodu poniżej.
Zadanie wydaje się łatwe – wystarczy usunąć liście zewnętrznej kropki, do list wewnętrznych dodać klasę, która jednak je uwzględni… stop! Mamy przecież selektory. Wystarczy zauważyć, że tylko lista będąca bezpośrednim dzieckiem obiektu o id „menu” ma mieć usunięte wypunktowanie (domyślnie jest widoczne). Wystarczy więc krótkie:
#menu>li {list-style:none}
Następną rzeczą, którą chcemy osiągnąć, jest oddzielenie głównych elementów listy poziomymi kreskami. Zadanie można by rozwiązać dokładnie tak, jak poprzednie, gdyby nie jeden szczegół – linie występują tylko między dwoma punktami listy. Nie ma natomiast żadnej linii nad ani pod całym menu.
I znów, można by stworzyć nową klasę, którą oznaczymy wszystkie elementy poza pierwszym (względnie ostatnim) i dodać im ramkę u góry, ale czy ma to sens, skoro znamy już zaawansowane selektory? Wystarczy nam proste, skuteczne i krótkie:
#menu>li+li {border-top: solid 2px #000}
To dopiero początek…
Problem został rozwiązany w jednej linijce, w dodatku bez naruszania struktury głównego dokumentu, co może okazać się bardzo ważne. Aby kod był jeszcze bardziej efektywny, można skorzystać z atrybutów (attribute selectors ). Oznacza się je nawiasem kwadratowym, a pozwalają na zarządzanie tylko tymi obiektami, które mają nadany atrybut. Np.:
p[title] { color:#f00; } //akapity mające ustawiony tytuł
div[class~=error] { color:#f00; } //kontenery o klasie zawierającej frazę "error"
p:not([class="obrazek"])//akapity, których klasa jest inna niż "obrazek"
Zastosowaliśmy poznane właśnie elementy, łącząc je w jednym, dość skomplikowanym jak na standardy CSS-a, ale bardzo efektywnym wywołaniu. W większości przypadków użycie selektorów dziedziczących i sąsiadujących umożliwia znaczące uproszczenie i skrócenie kodu. W dodatku zapobiega pomyłkom – nie musimy się martwić czy w przeszłości stosowano inne klasy i czy w przyszłości użytkownicy na pewno będą pamiętać o ich dodaniu. Dodatkowo, zastosowanie selektorów pozwala na znaczne skrócenie kodu.
Jak widać, CSS może być dużo bardziej efektywny niż się powszechnie uważa. Wystarczy tylko trochę praktyki, pomysłowości i znajomość trzech stosunkowo rzadko wykorzystywanych selektorów.