Kawałek Kodu

Kupujesz wysyłkowo lampkę odstraszającą owady, okazuje się, że żarówka jest spalona. Zamawiasz buty przez internet, za ciasne. Wreszcie rozpakowujesz paczkę, w której czeka wymarzy robot kuchenny, który będzie pasował pod kolor kuchni, dostajesz zupełnie inny kolor. Ręce opadają. Te opadają też czasem przy walce z przeciwnościami podczas programowania. Dziś pokażę Ci jedną z takich kłód rzucanych pod nogi, ale zastrzegam: nie jest to złośliwość twórców przeglądarek, ani też ich przeoczenie.

Zacznijmy od przykładu nawiązującego do tytułu.

<body>

<ul id="element1">
 <li></li>
 <li></li>
 <li></li>
</ul>

</body>

Spróbujmy teraz pobrać z powyższej struktury bezpośrednie dzieci #element1, które są tagami LI.

var li = document.getElementById("element1").querySelectorAll(">li");

I co otrzymałeś próbując wyświetlić wyniki z pomocą console.log?
Dlaczego tak się stało, o tym dalej.

Zmieńmy trochę kod JavaScript przy niezmienionym kodzie HTML:

var li = document.getElementById("element1").querySelectorAll("li");

A teraz jaki wynik otrzymałeś? Pewnie tablicę trzyelementową. Dlaczego więc dla selektora ogólnego metoda zwraca poprawny wynik, a dla selektora bezpośrednich dzieci zwraca błąd?

Spróbujmy to rozjaśnić następującym kodem:

<body>

<div>
 <p id="paragraf">
  <a href="#">link 1</a>
 </p>
</div>

<div>
 <a href="#">link 2</a>
</div>

</body>
var a = document.getElementById("paragraf").querySelectorAll("div a");

Jeśli sprawdzisz co otrzymałeś, to pewnie sobie pomyślisz, że przykład zamiast rozjaśnić sytuację, to jeszcze bardziej ją zaciemnił. A otrzymałeś pewnie jednoelementową tablicę z tagiem A.

Dlaczego?

Przecież w takim przypadku powinniśmy otrzymać pustą tablicę, bowiem element #paragraf nie ma dzieci DIV, a ten dzieci A. Dzieje się tak, ponieważ selektor zadziałał na całym dokumencie, a potem spośród elementów spełniających jego "warunek" odfiltrował elementy, które są dziećmi #paragraf. Czyli w pierwszym "kroku" metoda otrzymała 2 tagi A, a następnie wybrała spośród nich tylko te, które są dziećmi elementu P (czyli tylko jeden element A).

A co z tytułowymi selektorami?

Wróćmy więc do selektora >, zresztą analogiczna sytuacja jest z selektorem + (bezpośredniego sąsiada).
Dlaczego nie działa tak jak tego oczekujemy? Biorąc pod uwagę powyżej opisany sposób funkcjonowania, metoda najpierw wyszukałaby bezpośrednie dzieci dokumentu spełniające warunek selektora, a spośród nich odfiltrowałaby bezpośrednie dzieci #element1. Czy jest możliwe, aby elementy były jednocześnie bezpośrednimi dziećmi dokumentu oraz bezpośrednimi dziećmi #element1? A czy jest możliwe, aby w przypadku selektora +, element był bezpośrednim sąsiadem dokumentu i bezpośrednim sąsiadem #element1?

Teraz pewnie już znasz odpowiedź.
Tak przy okazji - sytuacja jest analogiczna w przypadku metody querySelector.

A czy jest jakieś rozwiązanie? Tak!

Pierwsze z nich polega na dodaniu do elementu, na którym wykonujemy metodę querySelector(All) atrybutu id, a następnie uwzględnienie w selektorze tego atrybutu:

var li = document.getElementById("element1").querySelectorAll("#element1>li");

Trochę to dziwne, ale działa.

A drugie? Drugie polega na dodaniu do selektora pseudoklasy :scope:

var li = document.getElementById("element1").querySelectorAll(":scope>li");

Pseudoklasa :scope powoduje przeniesienie działania metody z dokumentu do elementu, na którym metoda jest wywoływana.

Głowa do góry! Nie zawsze to czego nie chcemy, działa przeciwko nam. Jak to mówią, nie ma tego złego.

 

Przydatne linki:
"Hello dolar", czyli po co mi jQuery?
Metoda document.querySelectorAll
Metoda document.querySelector
Metoda element querySelectorAll
Metoda element.querySelector