Kurs HTML

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 i b 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 typu p 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 typu p.

: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.