Lekcja 3: jQuery - Przechodzenie po DOM (DOM Traversal)
Po wybraniu jednego lub więcej elementów za pomocą selektorów jQuery, często chcemy nawigować po drzewie DOM, aby znaleźć powiązane elementy – rodziców, dzieci, rodzeństwo itp. jQuery dostarcza bogaty zestaw metod do przechodzenia po strukturze dokumentu (DOM Traversal), co pozwala na precyzyjne docieranie do potrzebnych elementów.
Przechodzenie w górę drzewa DOM
Metody te pozwalają poruszać się od wybranych elementów w kierunku ich przodków.
.parent()
: Zwraca bezpośredniego rodzica każdego z wybranych elementów. Jeśli element ma wielu rodziców (co jest niemożliwe w standardowym HTML), zwróci ich wszystkich.$("span").parent(); // Zwraca bezpośredniego rodzica każdego elementu <span>
.parents(selektor)
: Zwraca wszystkich przodków każdego z wybranych elementów, aż do elementu<html>
. Można opcjonalnie podać selektor, aby odfiltrować tylko pasujących przodków.$("li").parents(); // Zwraca wszystkich przodków każdego <li> (np. <ul>, <div>, <body>, <html>) $("li").parents("div"); // Zwraca tylko tych przodków <li>, którzy są elementami <div>
.parentsUntil(selektor)
: Zwraca wszystkich przodków pomiędzy wybranym elementem a elementem pasującym do selektora (element pasujący do selektora nie jest dołączany).$("span").parentsUntil(".kontener"); // Zwraca przodków <span> aż do elementu z klasą "kontener" (ale bez niego)
.closest(selektor)
: Zaczynając od bieżącego elementu, przeszukuje w górę drzewa DOM (włączając sam element) i zwraca pierwszego przodka pasującego do selektora. Zwraca co najwyżej jeden element dla każdego elementu w początkowym zbiorze.$("a").closest("ul"); // Dla każdego linku <a> znajduje najbliższego przodka <ul>
Przechodzenie w dół drzewa DOM
Metody te pozwalają poruszać się od wybranych elementów w kierunku ich potomków.
.children(selektor)
: Zwraca bezpośrednie dzieci każdego z wybranych elementów. Można opcjonalnie podać selektor, aby odfiltrować tylko pasujące dzieci.$("ul").children(); // Zwraca wszystkie bezpośrednie dzieci (<li>) każdego <ul> $("div").children(".akapit"); // Zwraca bezpośrednie dzieci <div>, które mają klasę "akapit"
.find(selektor)
: Zwraca wszystkich potomków (na dowolnym poziomie zagnieżdżenia) każdego z wybranych elementów, którzy pasują do podanego selektora. Selektor jest wymagany.$("#main-content").find("p"); // Znajduje wszystkie paragrafy wewnątrz elementu o id="main-content" $("form").find("input, button"); // Znajduje wszystkie inputy i przyciski wewnątrz formularza
Przechodzenie na boki (po rodzeństwie)
Metody te pozwalają poruszać się między elementami na tym samym poziomie hierarchii (mającymi tego samego rodzica).
.siblings(selektor)
: Zwraca całe rodzeństwo (wszystkie elementy na tym samym poziomie, oprócz samego elementu) każdego z wybranych elementów. Można opcjonalnie podać selektor, aby odfiltrować tylko pasujące rodzeństwo.$("h2").siblings(); // Zwraca całe rodzeństwo każdego <h2> $("li.aktywny").siblings(":visible"); // Zwraca widoczne rodzeństwo elementu <li> z klasą "aktywny"
.next()
: Zwraca bezpośrednio następny element rodzeństwa dla każdego z wybranych elementów. Można opcjonalnie podać selektor, aby zwrócić następny element tylko jeśli pasuje.$("li:first").next(); // Zwraca drugi element <li> $("h2").next("p"); // Zwraca następny element po <h2>, tylko jeśli jest to <p>
.nextAll(selektor)
: Zwraca wszystkie następne elementy rodzeństwa dla każdego z wybranych elementów. Można opcjonalnie podać selektor.$("li:eq(1)").nextAll(); // Zwraca wszystkie <li> po drugim elemencie listy
.nextUntil(selektor)
: Zwraca wszystkie następne elementy rodzeństwa aż do elementu pasującego do selektora (ale bez niego).$("#start").nextUntil("#stop"); // Zwraca rodzeństwo pomiędzy elementami o id "start" i "stop"
.prev()
: Zwraca bezpośrednio poprzedni element rodzeństwa. Działa analogicznie do.next()
.$("li:last").prev(); // Zwraca przedostatni element <li>
.prevAll(selektor)
: Zwraca wszystkie poprzednie elementy rodzeństwa. Działa analogicznie do.nextAll()
.$("li:last").prevAll(); // Zwraca wszystkie <li> przed ostatnim
.prevUntil(selektor)
: Zwraca wszystkie poprzednie elementy rodzeństwa aż do elementu pasującego do selektora. Działa analogicznie do.nextUntil()
.
Filtrowanie
Metody te pozwalają zawęzić (przefiltrować) bieżący zbiór wybranych elementów.
.first()
: Zwraca tylko pierwszy element z bieżącego zbioru. Odpowiednik selektora:first
.$("li").first(); // Zwraca pierwszy element <li>
.last()
: Zwraca tylko ostatni element z bieżącego zbioru. Odpowiednik selektora:last
.$("li").last(); // Zwraca ostatni element <li>
.eq(index)
: Zwraca element o podanym indeksie (licząc od 0) z bieżącego zbioru. Indeksy ujemne liczą od końca (-1
to ostatni element).$("li").eq(2); // Zwraca trzeci element <li> $("li").eq(-1); // Zwraca ostatni element <li>
.filter(selektor | funkcja)
: Zmniejsza zbiór elementów do tych, które pasują do selektora lub dla których funkcja zwrócitrue
.$("li").filter(".aktywny"); // Zwraca tylko te <li>, które mają klasę "aktywny" $("input").filter(function() { return this.value.length > 5; // Zwraca inputy, których wartość ma więcej niż 5 znaków });
.not(selektor | funkcja)
: Usuwa elementy ze zbioru, które pasują do selektora lub dla których funkcja zwrócitrue
. Przeciwieństwo.filter()
.$("li").not(":first-child"); // Zwraca wszystkie <li> oprócz pierwszego dziecka każdego rodzica $("p").not(".ostrzezenie"); // Zwraca paragrafy, które nie mają klasy "ostrzezenie"
.has(selektor)
: Zmniejsza zbiór elementów do tych, które zawierają potomka pasującego do selektora.$("div").has("img"); // Zwraca tylko te <div>, które zawierają jakiś obrazek
.slice(start, end)
: Zmniejsza zbiór elementów do podzbioru określonego przez indeksy początkowy (włącznie) i końcowy (wyłącznie).$("li").slice(1, 4); // Zwraca drugi, trzeci i czwarty element <li> (indeksy 1, 2, 3)
Łańcuchowanie Metod (Chaining)
Większość metod jQuery zwraca obiekt jQuery (często zmodyfikowany zbiór elementów), co pozwala na wywoływanie kolejnych metod jQuery na wyniku poprzedniej. Nazywa się to łańcuchowaniem (chaining) i pozwala pisać zwięzły i czytelny kod.
$("ul.menu") // Wybierz listę z klasą "menu"
.children("li") // Wybierz jej bezpośrednie dzieci <li>
.first() // Weź tylko pierwszy element <li>
.addClass("pierwszy-element") // Dodaj mu klasę
.next() // Przejdź do następnego rodzeństwa (<li>)
.css("color", "red"); // Zmień jego kolor na czerwony
Zadanie praktyczne
Stwórz strukturę HTML z zagnieżdżonymi listami:
<div id="kontener">
<ul class="lista-glowna">
<li>Punkt 1</li>
<li class="aktywny">
Punkt 2
<ul class="podlista">
<li>Podpunkt 2.1</li>
<li>Podpunkt 2.2</li>
</ul>
</li>
<li>Punkt 3</li>
</ul>
<p>Jakiś tekst poza listą.</p>
</div>
Używając metod przechodzenia po DOM w jQuery:
- Znajdź element
<li>
z klasą "aktywny". - Od tego elementu, znajdź jego rodzica
<ul>
i dodaj mu klasę "rodzic-aktywnego". - Od elementu "aktywny", znajdź jego bezpośrednie dzieci
<ul>
(podlistę) i dodaj jej klasę "podlista-aktywna". - Od elementu "aktywny", znajdź jego następne rodzeństwo
<li>
(Punkt 3) i zmień jego tekst na "Następny po aktywnym". - Wybierz wszystkie elementy
<li>
wewnątrz elementu o id "kontener" i dodaj im klasę "element-kontenera".
Pokaż rozwiązanie
$(function() {
// 1. Znajdź element li.aktywny
const aktywnyLi = $("li.aktywny");
console.log("Znaleziono aktywny element:", aktywnyLi);
// 2. Znajdź rodzica ul i dodaj klasę
aktywnyLi.parent().addClass("rodzic-aktywnego");
console.log("Dodano klasę rodzicowi aktywnego elementu.");
// 3. Znajdź podlistę ul i dodaj klasę
aktywnyLi.children("ul").addClass("podlista-aktywna");
console.log("Dodano klasę podliście aktywnego elementu.");
// 4. Znajdź następne rodzeństwo li i zmień tekst
aktywnyLi.next().text("Następny po aktywnym");
console.log("Zmieniono tekst następnego rodzeństwa.");
// 5. Wybierz wszystkie li w kontenerze i dodaj klasę (używając find)
$("#kontener").find("li").addClass("element-kontenera");
console.log("Dodano klasę wszystkim li w kontenerze.");
});
Możesz dodać odpowiednie style CSS dla klas "rodzic-aktywnego", "podlista-aktywna" i "element-kontenera", aby zobaczyć efekty wizualne.
Zadanie do samodzielnego wykonania
Mając strukturę HTML z poprzedniego zadania, wykonaj następujące operacje używając jQuery:
- Wybierz "Podpunkt 2.2".
- Używając
.closest()
, znajdź najbliższy element<div>
będący jego przodkiem i dodaj mu klasę "najblizszy-div". - Używając
.siblings()
, znajdź rodzeństwo "Podpunktu 2.2" (czyli "Podpunkt 2.1") i zmień jego kolor tekstu na czerwony. - Wybierz wszystkie elementy
<li>
w głównej liście (.lista-glowna
) i używając.not()
, usuń ze zbioru element z klasą "aktywny", a pozostałym dodaj klasę "nieaktywny".
FAQ - Przechodzenie po DOM w jQuery
Jaka jest różnica między .children()
a .find()
?
.children()
przeszukuje tylko bezpośrednie dzieci wybranego elementu. .find()
przeszukuje wszystkich potomków na dowolnym poziomie zagnieżdżenia. .find()
wymaga podania selektora, podczas gdy .children()
może go nie mieć (zwróci wtedy wszystkie bezpośrednie dzieci).
Jaka jest różnica między .parent()
a .parents()
?
.parent()
zwraca tylko bezpośredniego rodzica. .parents()
zwraca wszystkich przodków aż do korzenia dokumentu (<html>
). Obie metody mogą być opcjonalnie filtrowane przez selektor.
Do czego przydaje się .closest()
?
.closest()
jest bardzo użyteczne, gdy mamy element (np. kliknięty przycisk wewnątrz złożonej struktury) i chcemy znaleźć najbliższy element nadrzędny określonego typu (np. cały wiersz tabeli <tr>
, kontener produktu <div class="produkt">
), aby wykonać na nim operację.
Czy metody przechodzenia modyfikują oryginalny zbiór elementów?
Nie, metody przechodzenia (np. .parent()
, .children()
, .next()
) zwracają nowy obiekt jQuery zawierający znalezione elementy. Oryginalny obiekt jQuery pozostaje niezmieniony, chyba że użyjesz metod modyfikujących w miejscu, jak np. .add()
(rzadziej używane).
Jak wrócić do poprzedniego zbioru elementów po przejściu (np. po .find()
)?
jQuery udostępnia metodę .end()
. Przywraca ona stan obiektu jQuery do tego sprzed ostatniej operacji przechodzenia, która modyfikowała zbiór elementów. Pozwala to kontynuować łańcuchowanie na poprzednim zbiorze.
$("ul")
.find("li") // Wybiera wszystkie li wewnątrz ul
.addClass("podpunkt") // Dodaje klasę do li
.end() // Wraca do zbioru ul
.addClass("przetworzona-lista"); // Dodaje klasę do ul