10.5. Pseudoklasy strukturalne
Wprowadzenie: Wybieranie na podstawie pozycji wśród rodzeństwa
Pseudoklasy strukturalne pozwalają wybierać elementy na podstawie ich **pozycji** w grupie rodzeństwa (elementów mających tego samego rodzica) lub ich **relacji** z rodzeństwem.
Są one niezwykle przydatne do stylizowania elementów list, wierszy tabel, akapitów w artykule itp., bez konieczności dodawania dodatkowych klas CSS.
:first-child
i :last-child
:first-child
: Wybiera element, który jest **pierwszym dzieckiem** swojego rodzica.:last-child
: Wybiera element, który jest **ostatnim dzieckiem** swojego rodzica.
<ul>
<li>Pierwszy element listy</li>
<li>Drugi element listy</li>
<li>Trzeci element listy</li>
<li>Ostatni element listy</li>
</ul>
<div>
<p>Pierwszy paragraf w div.</p>
<h3>Podtytuł (nie pierwszy paragraf)</h3>
<p>Drugi paragraf.</p>
</div>
/* Pogrub pierwszy element KAŻDEJ listy */
li:first-child {
font-weight: bold;
}
/* Podkreśl ostatni element KAŻDEJ listy */
li:last-child {
text-decoration: underline;
}
/* Nadaj zielony kolor paragrafowi, jeśli jest PIERWSZYM dzieckiem */
p:first-child {
color: green;
}
W powyższym przykładzie:
- "Pierwszy element listy" zostanie pogrubiony.
- "Ostatni element listy" zostanie podkreślony.
- "Pierwszy paragraf w div." będzie zielony, ponieważ jest pierwszym dzieckiem
div
. Drugi paragraf nie jest pierwszym dzieckiem.
Ważne: :first-child
i :last-child
sprawdzają, czy element jest pierwszym/ostatnim dzieckiem **ogólnie**, a nie tylko pierwszym/ostatnim elementem danego typu.
:nth-child(an+b)
Pseudoklasa :nth-child()
jest bardzo potężna. Wybiera elementy na podstawie ich **pozycji** wśród rodzeństwa, licząc od początku.
Przyjmuje argument w formacie an+b
, gdzie:
a
ib
to liczby całkowite (mogą być 0 lub ujemne).n
zaczyna od 0 i rośnie (0, 1, 2, 3...).- Formuła
an+b
generuje sekwencję liczb (indeksów dzieci, licząc od 1).
Najczęstsze zastosowania:
:nth-child(liczba)
: Wybiera konkretne dziecko, np.:nth-child(3)
wybiera trzecie dziecko.:nth-child(even)
lub:nth-child(2n)
: Wybiera parzyste dzieci (2, 4, 6...).:nth-child(odd)
lub:nth-child(2n+1)
: Wybiera nieparzyste dzieci (1, 3, 5...).:nth-child(n+3)
: Wybiera wszystkie dzieci od trzeciego wzwyż (3, 4, 5...).:nth-child(-n+3)
: Wybiera pierwsze trzy dzieci (3, 2, 1).:nth-child(3n)
: Wybiera co trzecie dziecko (3, 6, 9...).
<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ol>
/* Wybierz trzeci element li */
li:nth-child(3) {
background-color: lightblue;
}
/* Wybierz parzyste elementy li */
li:nth-child(even) {
color: gray;
}
/* Wybierz pierwsze trzy elementy li */
li:nth-child(-n+3) {
font-style: italic;
}
Ważne: Podobnie jak :first-child
, :nth-child()
liczy **wszystkie** dzieci, niezależnie od ich typu. Jeśli chcesz liczyć tylko elementy danego typu, użyj :nth-of-type()
.
:nth-of-type(an+b)
Pseudoklasa :nth-of-type()
działa bardzo podobnie do :nth-child()
, ale z kluczową różnicą: **liczy tylko te elementy rodzeństwa, które są tego samego typu co element, do którego stosujemy pseudoklasę**.
Argument an+b
działa tak samo jak w :nth-child()
.
<div class="mixed-content">
<p>Pierwszy paragraf</p>
<h3>Nagłówek</h3>
<p>Drugi paragraf</p>
<p>Trzeci paragraf</p>
<div>Inny div</div>
<p>Czwarty paragraf</p>
</div>
/* Wybierz DRUGI paragraf (p) wśród rodzeństwa */
.mixed-content p:nth-of-type(2) {
color: purple;
}
/* Wybierz co drugi paragraf (p) wśród rodzeństwa */
.mixed-content p:nth-of-type(2n) {
background-color: lightyellow;
}
/* Dla porównania - :nth-child(2) wybrałby h3, bo jest drugim dzieckiem ogólnie */
.mixed-content :nth-child(2) {
/* border: 1px solid red; */ /* To wybrałoby h3 */
}
W powyższym przykładzie:
p:nth-of-type(2)
wybierze "Drugi paragraf", ponieważ jest to drugi element typup
wśród dzieci.mixed-content
.p:nth-of-type(2n)
wybierze "Drugi paragraf" i "Czwarty paragraf", ponieważ są to odpowiednio drugi i czwarty element typup
.
:nth-of-type()
jest często bardziej intuicyjne, gdy chcemy stylizować np. co drugi wiersz tabeli (tr:nth-of-type(even)
) lub co drugi paragraf w artykule, ignorując inne elementy (nagłówki, obrazki) znajdujące się między nimi.
:only-child
i :only-of-type
:only-child
: Wybiera element, który jest **jedynym dzieckiem** swojego rodzica (nie ma żadnego rodzeństwa).:only-of-type
: Wybiera element, który jest **jedynym elementem swojego typu** wśród rodzeństwa.
<div><p>Jedyny paragraf w tym div</p></div>
<div>
<p>Paragraf 1</p>
<p>Paragraf 2</p>
</div>
<div>
<h3>Nagłówek</h3>
<p>Jedyny paragraf, ale nie jedyne dziecko</p>
</div>
/* Wybierz paragraf, jeśli jest JEDYNYM dzieckiem */
p:only-child {
font-weight: bold;
color: darkorange;
}
/* Wybierz paragraf, jeśli jest JEDYNYM paragrafem wśród rodzeństwa */
p:only-of-type {
text-decoration: underline;
}
W powyższym przykładzie:
- Paragraf w pierwszym
div
zostanie pogrubiony, pomarańczowy i podkreślony (bo jest jedynym dzieckiem i jedynym paragrafem). - Paragrafy w drugim
div
nie zostaną wybrane przez żadną z tych pseudoklas. - Paragraf w trzecim
div
zostanie tylko podkreślony (bo jest jedynym paragrafem, ale nie jedynym dzieckiem).
:empty
Pseudoklasa :empty
wybiera elementy, które **nie mają żadnych dzieci**, w tym również **nie zawierają tekstu**. Białe znaki (spacje, nowe linie) są ignorowane.
Jest przydatna do ukrywania pustych elementów lub nadawania im specjalnego stylu (np. tła informującego o braku treści).
<div class="box empty-box"></div> <!-- Pusty -->
<div class="box whitespace-box"> </div> <!-- Pusty (tylko białe znaki) -->
<div class="box comment-box"><!-- Komentarz --></div> <!-- Pusty (komentarze są ignorowane) -->
<div class="box text-box">Jakiś tekst</div>
<div class="box child-box"><span></span></div>
.box {
min-height: 30px; border: 1px solid #ccc; margin-bottom: 5px; padding: 5px;
}
/* Wybierz puste elementy .box */
.box:empty {
background-color: lightpink;
border-style: dashed;
}
/* Można też ukryć puste elementy */
/* p:empty { display: none; } */
W tym przykładzie, pierwsze trzy divy (.empty-box
, .whitespace-box
, .comment-box
) zostaną ostylowane jako puste (różowe tło, przerywana ramka), ponieważ nie zawierają ani tekstu, ani innych elementów (komentarze i białe znaki są ignorowane).
Zadania praktyczne
Zadanie 1 (z rozwiązaniem)
Stwórz tabelę z kilkoma wierszami (tr
). Użyj :nth-child(even)
, aby nadać co drugiemu wierszowi tabeli (tr
) lekko szare tło (efekt "zebry").
Rozwiązanie:
HTML:
<table>
<tbody>
<tr><td>Wiersz 1</td></tr>
<tr><td>Wiersz 2</td></tr>
<tr><td>Wiersz 3</td></tr>
<tr><td>Wiersz 4</td></tr>
</tbody>
</table>
CSS:
table {
border-collapse: collapse; /* Dla lepszego wyglądu */
width: 100%;
}
td { padding: 5px; border: 1px solid #ccc; }
tr:nth-child(even) { /* Wybiera parzyste wiersze */
background-color: #f2f2f2; /* Lekko szare tło */
}
Efekt: Wiersze 2 i 4 będą miały szare tło.
Zadanie 2 (do samodzielnego wykonania)
Stwórz listę ul
. Użyj :first-child
i :last-child
, aby nadać inne style (np. kolor tła) pierwszemu i ostatniemu elementowi listy.
Zadanie 3 (do samodzielnego wykonania)
Stwórz sekcję z kilkoma paragrafami p
i jednym obrazkiem img
. Użyj :nth-of-type(odd)
, aby nadać styl (np. kursywę) nieparzystym paragrafom w tej sekcji, ignorując obrazek.
Zadanie 4 (do samodzielnego wykonania)
Stwórz kilka elementów div
, niektóre z nich pozostaw puste. Użyj :empty
, aby ukryć puste divy (display: none;
).
FAQ - Najczęściej zadawane pytania
Jaka jest główna różnica między :nth-child()
a :nth-of-type()
?
:nth-child(n)
wybiera element, który jest n-tym dzieckiem swojego rodzica, **niezależnie od typu elementu**. :nth-of-type(n)
wybiera element, który jest n-tym elementem **swojego typu** wśród rodzeństwa. :nth-of-type()
jest często bardziej przydatne, gdy w kontenerze znajdują się elementy różnych typów.
Czy indeksy w :nth-child()
i :nth-of-type()
zaczynają się od 0 czy 1?
Indeksy liczone są **od 1**. Dlatego :nth-child(1)
jest równoważne :first-child
.
Czy :empty
uwzględnia komentarze HTML lub białe znaki?
Nie, pseudoklasa :empty
ignoruje zarówno komentarze HTML (<!-- ... -->
), jak i białe znaki (spacje, tabulatory, nowe linie) wewnątrz elementu. Element jest uważany za pusty, jeśli nie zawiera żadnych innych elementów ani tekstu (poza białymi znakami).
Czy mogę użyć :nth-last-child()
i :nth-last-of-type()
?
Tak, istnieją również pseudoklasy :nth-last-child(an+b)
i :nth-last-of-type(an+b)
, które działają analogicznie do swoich odpowiedników bez "last", ale liczą elementy **od końca** grupy rodzeństwa. Na przykład li:nth-last-child(1)
jest równoważne li:last-child
.