Najlepszy sposób na zdobycie węzłów potomnych

233

Zastanawiałem się, JavaScript oferuje wiele metod uzyskania pierwszego elementu potomnego z dowolnego elementu, ale który jest najlepszy? Mówiąc najlepiej, mam na myśli: najbardziej zgodny z wieloma przeglądarkami, najszybszy, najbardziej wszechstronny i przewidywalny, jeśli chodzi o zachowanie. Lista metod / właściwości, których używam jako aliasów:

var elem = document.getElementById('container');
var child = elem.children[0];
var child = elem.firstElementChild; // == children[0]

Działa to w obu przypadkach:

var child = elem.childNodes[0]; // or childNodes[1], see below

Tak jest w przypadku formularzy lub <div>iteracji. Jeśli mogę napotkać elementy tekstowe:

var child = elem.childNodes; // treat as NodeList
var child = elem.firstChild;

O ile mogę wypracować, firstChildużywa NodeList childNodesi firstElementChildużywa children. Opieram to założenie na referencji MDN:

childNodejest odniesieniem do pierwszego elementu potomnego węzła elementu lub nulljeśli go nie ma.

Zgaduję, że pod względem prędkości różnica, jeśli w ogóle, będzie prawie zerowa, ponieważ firstElementChildjest to faktycznie odniesienie children[0], a childrenobiekt i tak jest już w pamięci.

To, co mnie rzuca, to childNodesprzedmiot. Użyłem go, aby spojrzeć na formularz w elemencie tabeli. Chociaż childrenwyświetla listę wszystkich elementów formularza, childNodeswydaje się, że zawiera również spacje z kodu HTML:

console.log(elem.childNodes[0]);
console.log(elem.firstChild);

Oba dzienniki <TextNode textContent="\n ">

console.log(elem.childNodes[1]);
console.log(elem.children[0]);
console.log(elem.firstElementChild);

Wszystko dziennika <input type="text"... >. Dlaczego? Zrozumiałbym, że jeden obiekt pozwoliłby mi pracować z „surowym” kodem HTML, podczas gdy drugi przykleja się do DOM, ale childNodeselement wydaje się działać na obu poziomach.

Wracając do mojego początkowego pytania, domyślam się: jeśli chcę najbardziej kompleksowy obiekt, childNodesjest to droga, ale ze względu na jego kompleksowość może nie być najbardziej przewidywalny pod względem zwrotu elementu, który chcę / oczekuj w dowolnym momencie. Wsparcie w różnych przeglądarkach może również okazać się wyzwaniem, chociaż mogę się mylić.

Czy ktoś mógłby wyjaśnić różnicę między przedmiotami? Jeśli jest różnica prędkości, choć nieznaczna, też chciałbym wiedzieć. Jeśli widzę, że to wszystko źle, nie krępuj się mnie uczyć.


PS: Proszę, lubię JavaScript, więc tak, chcę poradzić sobie z tego rodzaju sprawami. Odpowiedzi typu „jQuery zajmuje się tym dla Ciebie” nie są tym, czego szukam, dlatego nie etykietka.

Elias Van Ootegem
źródło
49
>> proszę, proszę, lubię JavaScript, więc tak, chcę poradzić sobie z tego rodzaju sprawami. odpowiedzi typu „jQuery zajmuje się tym dla ciebie” nie są tym, czego szukam << głosy poparcia dla tego
david.barkhuizen

Odpowiedzi:

211

Wygląda na to, że przesadzasz. Zauważyłeś różnicę między childNodesi children, która childNodeszawiera wszystkie węzły, w tym węzły tekstowe składające się wyłącznie z białych znaków, podczas gdy childrenjest to zbiór tylko węzłów potomnych, które są elementami. To naprawdę wszystko.

Nie ma nic nieprzewidywalnego w żadnej z tych kolekcji, choć należy pamiętać o kilku kwestiach:

  • IE <= 8 nie zawiera węzłów tekstowych zawierających wyłącznie spacje, childNodespodczas gdy inne przeglądarki to robią
  • IE <= 8 zawiera węzły komentarzy wewnątrz, childrenpodczas gdy inne przeglądarki mają tylko elementy

children, firstElementChilda znajomi to tylko wygody, prezentujące odfiltrowany widok DOM ograniczony tylko do elementów.

Tim Down
źródło
Zrozumiałem tyle, choć jedno wciąż mnie wkurza: biały tekst, który rozpoznaje childNodes, to tak naprawdę nowa linia i kilka zakładek w kodzie. Czy jest to spowodowane typem XHTML? czy można to rozwiązać, umieszczając biały znak w strukturze kodu w tagach?
Elias Van Ootegem,
@EliasVanOotegem: To nie ma związku z doctype. Węzły białych znaków są zawsze zawarte w DOM (oprócz IE <9) zarówno dla HTML, jak i XHTML, niezależnie od tego, gdzie występują w źródle. Ogólnie jest to dobra rzecz, chociaż może cię złapać, jeśli nie jesteś na to przygotowany.
Tim Down
Miałem na myśli, czy pomogłoby to tak napisać moje znaczniki <td\n\t>content</td>. Tak jak w przypadku XML, aby uniknąć traktowania nadmiaru białych znaków jako części danych (w tym przypadku DOM). Dlaczego spacje wewnątrz tagu powinny być uwzględnione w DOM?
Elias Van Ootegem
@EliasVanOotegem: Oh, rozumiem. Białe znaki po nazwie znacznika wewnątrz znacznika są ignorowane, więc możesz to zrobić. Widziałem tę technikę stosowaną w precyzyjnych układach, aby zapobiec dodawaniu przez przeglądarki niewielkich odstępów między białymi elementami.
Tim Down
2
@Christophe: Ach, słuszne dzięki, dzięki. Dodam notatkę do mojej odpowiedzi. Biorąc pod uwagę, że childrenpowstał on w IE 4 ponad dekadę, zanim stał się standardem lub został przyjęty przez inne przeglądarki, możesz argumentować, że IE <= 8 jest poprawny, a inne przeglądarki są błędne.
Tim Down
23

firstElementChild może być niedostępny w IE <9 (tylko firstChild)

w IE <9 firstChild jest firstElementChild, ponieważ MS DOM (IE <9) nie przechowuje pustych węzłów tekstowych. Ale jeśli zrobisz to w innych przeglądarkach, zwrócą puste węzły tekstowe ...

moje rozwiązanie

child=(elem.firstElementChild||elem.firstChild)

da to pierwszemu dziecku nawet w IE <9

neu-rah
źródło
Jeśli elem.firstChildnie jest węzłem elementu, wyniki będą inne w starych IE, ponieważ elem.firstElementChildzawsze jest węzłem elementu.
duri
Przykro mi, ale wiem, jak zdobyć pierwsze dziecko we wszystkich głównych przeglądarkach. Chcę wiedzieć, jak są zbudowane różne obiekty, aby lepiej zrozumieć podobieństwa i różnice między nimi. Później
zmienię
1
firstElementChildniekoniecznie jest również bezpieczny w przeglądarkach innych niż IE: Firefox zaczął obsługiwać go tylko w wersji 3.5.
Tim Down
działa to w przypadku chrome, IE9 i IE8, ponieważ jeśli mają one firstElementChild, wówczas inne sprawdzenie nie zostanie wykonane, w przeciwnym razie (stary IE) mamy firstChild i jest to węzeł elementu dla przeglądarki, który nie ma firstElementChild (IE <9 ) ... nadal zastanawiam się nad FF
neu-rah
1
Tim ma rację, kiedy przeglądałem rzeczy na tych obiektach, to się pojawiło, dobra strona do sprawdzenia raz na jakiś czas
Elias Van Ootegem
20

Można to zrobić za pomocą przeglądarki, childNodesaby uzyskać NodeList, a następnie utworzyć tablicę wszystkich węzłów za pomocą nodeType ELEMENT_NODE .

/**
 * Return direct children elements.
 *
 * @param {HTMLElement}
 * @return {Array}
 */
function elementChildren (element) {
    var childNodes = element.childNodes,
        children = [],
        i = childNodes.length;

    while (i--) {
        if (childNodes[i].nodeType == 1) {
            children.unshift(childNodes[i]);
        }
    }

    return children;
}

http://jsfiddle.net/s4kxnahu/

Jest to szczególnie łatwe, jeśli używasz biblioteki narzędziowej, takiej jak lodash :

/**
 * Return direct children elements.
 *
 * @param {HTMLElement}
 * @return {Array}
 */
function elementChildren (element) {
    return _.where(element.childNodes, {nodeType: 1});
}

Przyszłość:

Możesz używać querySelectorAllw połączeniu z :scopepseudoklasą (pasuje do elementu, który jest punktem odniesienia selektora):

parentElement.querySelectorAll(':scope > *');

W momencie pisania tego :scopejest obsługiwany w Chrome, Firefox i Safari.

Gajus
źródło
: zakres został usunięty ze specyfikacji . Czy powinniśmy usunąć sekcję Przyszłość ?
Thomas Marti
4

Aby dodać do innych odpowiedzi, nadal istnieją tutaj istotne różnice, szczególnie w przypadku <svg>elementów.

Użyłem obu .childNodesi .childrenwolałem pracować z HTMLCollectiondostarczonym przez .childrengettera.

Jednak dzisiaj napotkałem problemy z awarią IE / Edge podczas korzystania .childrenz <svg>. Chociaż .childrenjest obsługiwany w IE w podstawowych elementach HTML, nie jest obsługiwany w dokumentach / fragmentach dokumentów ani elementach SVG .

Dla mnie byłem w stanie po prostu pobrać potrzebne elementy, .childNodes[n]ponieważ nie muszę się martwić dodatkowymi węzłami tekstowymi. Możesz zrobić to samo, ale jak wspomniano powyżej, nie zapominaj, że możesz napotkać nieoczekiwane elementy.

Mam nadzieję, że jest to pomocne dla kogoś, kto drapie się po głowie, próbując dowiedzieć się, dlaczego .childrendziała gdzie indziej w swoim js na nowoczesnym IE, a zawiesza elementy dokumentu lub SVG.

slothluvchunk
źródło
3

Hej, chłopaki, nie dajcie się zwieść białej przestrzeni. po prostu przetestuj to w przeglądarce konsoli. użyj natywnego javascript. Oto i przykład z dwoma zestawami „ul” o tej samej klasie. Nie musisz mieć listy „ul” w jednym wierszu, aby uniknąć spacji, po prostu użyj liczby tablic, aby przeskoczyć nad białą spacją.

Jak ominąć białą przestrzeń querySelector()następnie childNodes[]js skrzypce link: https://jsfiddle.net/aparadise/56njekdo/

var y = document.querySelector('.list');
var myNode = y.childNodes[11].style.backgroundColor='red';

<ul class="list">
    <li>8</li>
    <li>9</li>
    <li>100</li>
</ul>

<ul class="list">
    <li>ABC</li>
    <li>DEF</li>
    <li>XYZ</li>
</ul>
andre paradise
źródło
Nie moje głosowanie w dół, ale myślę, że ktoś głosował w dół, ponieważ: a) To pytanie ma 4 lata i document.querySelectornie było wtedy dobrze poparte. b) Pytanie polega w zasadzie na zadaniu różnic między wewnętrznymichildren i childNodes wewnętrznymi , co jest bardziej wiarygodne, kiedy wybrać jedną z drugiej. oraz c) Ponieważ, jak demonstraded w pytaniu: childNodes czy to odstępy węzłów, childrennie ...
Elias Van Ootegem