Kurs HTML

10.9. Łączenie selektorów zaawansowanych

Strona główna > Kurs CSS > Rozdział 10: Zaawansowane Selektory CSS > Łą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).