10.9. Łączenie selektorów zaawansowanych
Wprowadzenie: Tworzenie precyzyjnych reguł
Siła CSS tkwi nie tylko w poszczególnych selektorach, ale przede wszystkim w możliwości ich **łączenia**. Kombinując selektory typu, klas, ID, atrybutów, pseudoklas oraz selektory relacyjne (potomka, dziecka, rodzeństwa), możemy tworzyć niezwykle precyzyjne reguły, które wybierają dokładnie te elementy, które chcemy ostylować, w określonym kontekście.
W tej lekcji podsumujemy i pokażemy przykłady łączenia różnych poznanych dotąd selektorów zaawansowanych.
Kombinacje selektorów relacyjnych i atrybutów/pseudoklas
Możemy łatwo łączyć selektory relacyjne (spacja, >, +, ~) z selektorami atrybutów i pseudoklasami, aby zawęzić wybór do elementów spełniających dodatkowe warunki.
/* Wybierz linki (a) z atrybutem target="_blank", */
/* które są dziećmi elementów li wewnątrz nav */
nav li > a[target="_blank"] {
/* Dodaj ikonkę "nowe okno" */
padding-right: 18px;
background: url('external-link-icon.png') no-repeat right center;
}
/* Wybierz paragrafy (p), które są bezpośrednio po h2 */
/* i NIE są pierwszym dzieckiem swojego rodzica */
h2 + p:not(:first-child) {
margin-top: 0.5em;
}
/* Wybierz inputy typu "checkbox", które są zaznaczone (:checked) */
/* i znajdują się po etykiecie (label) */
label + input[type="checkbox"]:checked {
box-shadow: 0 0 3px green;
}
/* Wybierz wszystkie elementy li, które są nieparzyste (:nth-child(odd)) */
/* i znajdują się po elemencie li z klasą .highlight */
li.highlight ~ li:nth-child(odd) {
background-color: lightyellow;
}
Łańcuchy selektorów
Możemy tworzyć długie łańcuchy selektorów, aby precyzyjnie określić ścieżkę do elementu w drzewie DOM i jego stan.
/* Wybierz element span z klasą .price, który jest potomkiem */
/* elementu div z klasą .product-info, który jest dzieckiem */
/* elementu li z klasą .product-item, który jest nieparzystym dzieckiem */
/* listy ul z ID #products-list */
ul#products-list > li.product-item:nth-child(odd) div.product-info span.price {
color: darkred;
font-weight: bold;
}
/* Wybierz link (a), który ma fokus (:focus), */
/* jest dzieckiem elementu li, który jest potomkiem */
/* elementu nav, który NIE ma klasy .footer-nav */
nav:not(.footer-nav) li > a:focus {
outline: 2px dotted blue;
}
Chociaż takie złożone selektory są możliwe, należy ich używać z umiarem. Bardzo długie i skomplikowane selektory mogą:
- Być trudne do odczytania i zrozumienia.
- Być mało wydajne (choć w nowoczesnych przeglądarkach rzadko stanowi to problem).
- Być kruche - niewielka zmiana w strukturze HTML może sprawić, że selektor przestanie działać.
- Mieć bardzo wysoką specyficzność, co utrudnia późniejsze nadpisywanie stylów.
Często lepszym podejściem jest dodanie odpowiedniej klasy CSS do elementu, który chcemy ostylować, i użycie prostszego selektora klasy.
Kolejność i specyficzność
Pamiętajmy o zasadach kaskadowości i specyficzności. Gdy łączymy wiele selektorów, specyficzność całej reguły rośnie.
- Selektory ID mają najwyższą specyficzność.
- Selektory klas, atrybutów i pseudoklas mają średnią specyficzność.
- Selektory typu i pseudoelementy mają najniższą specyficzność.
Specyficzność złożonego selektora jest sumą specyficzności jego części składowych. Na przykład div#main .article > p:first-child
ma specyficzność: 1 ID (#main
), 1 klasa (.article
), 1 pseudoklasa (:first-child
) i 2 selektory typu (div
, p
).
Zrozumienie specyficzności jest kluczowe przy debugowaniu, dlaczego dany styl nie jest stosowany lub jest nadpisywany przez inną regułę.
Zadania praktyczne
Zadanie 1 (z rozwiązaniem)
Stwórz menu nawigacyjne (nav > ul > li > a
). Użyj kombinacji selektorów, aby nadać inny styl linkowi wewnątrz elementu li
, który ma klasę .active
, ale tylko wtedy, gdy użytkownik najedzie na niego myszą (:hover
).
Rozwiązanie:
HTML:
<nav>
<ul>
<li><a href="#">Home</a></li>
<li class="active"><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
CSS:
nav ul li a {
display: block; padding: 10px; text-decoration: none; color: #333;
}
nav ul li.active a {
font-weight: bold; /* Styl dla aktywnego linku */
}
/* Styl dla aktywnego linku PO NAJECHANIU */
nav ul li.active > a:hover {
background-color: lightgoldenrodyellow;
color: black;
}
Efekt: Link "About" będzie domyślnie pogrubiony. Po najechaniu na niego myszą, dodatkowo zmieni się jego tło i kolor tekstu.
Zadanie 2 (do samodzielnego wykonania)
Stwórz tabelę. Użyj kombinacji selektorów, aby nadać specjalne tło komórkom td
w nieparzystych wierszach (tr:nth-child(odd)
), ale tylko tym komórkom, które **nie są** pierwszą komórką w wierszu (td:not(:first-child)
).
Zadanie 3 (do samodzielnego wykonania)
Stwórz formularz z polami input. Użyj kombinacji selektorów, aby nadać zielone obramowanie polu input, które jest wymagane (:required
) i aktualnie ma fokus (:focus
).
FAQ - Najczęściej zadawane pytania
Czy kolejność selektorów w łańcuchu ma znaczenie?
Tak, kolejność ma kluczowe znaczenie. Selektory są czytane i dopasowywane zgodnie z ich pozycją w łańcuchu, odzwierciedlając strukturę DOM lub stan elementu. Na przykład div p
to co innego niż p div
.
Jak unikać zbyt skomplikowanych selektorów?
Staraj się używać klas CSS w sposób semantyczny i modułowy (np. metodologia BEM). Zamiast tworzyć długi selektor oparty na strukturze DOM, nadaj docelowemu elementowi odpowiednią klasę i użyj prostego selektora klasy. Używaj selektorów relacyjnych i pseudoklas tylko wtedy, gdy jest to logiczne i upraszcza kod.
Czy mogę łączyć pseudoklasy z pseudoelementami?
Tak. Można na przykład stylizować pseudoelement ::before
tylko wtedy, gdy element nadrzędny jest w stanie :hover
: button:hover::before { content: '>'; }
. Pseudoelement zawsze występuje na końcu selektora.
Jak debugować złożone selektory?
Narzędzia deweloperskie przeglądarki są tu nieocenione. Możesz zbadać element i zobaczyć, które reguły CSS są do niego stosowane, jaka jest ich specyficzność i które zostały nadpisane. Możesz też testować selektory bezpośrednio w konsoli (np. używając document.querySelectorAll()
w JavaScript).