Der beste Weg, um untergeordnete Knoten zu erhalten

233

Ich habe mich gefragt, ob JavaScript eine Vielzahl von Methoden bietet, um das erste untergeordnete Element aus einem Element abzurufen. Aber welches ist das beste? Mit "am besten" meine ich: am meisten browserübergreifend kompatibel, am schnellsten, am umfassendsten und vorhersehbarsten, wenn es um Verhalten geht. Eine Liste der Methoden / Eigenschaften, die ich als Aliase verwende:

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

Dies funktioniert in beiden Fällen:

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

Dies ist bei Formularen oder <div>Iterationen der Fall . Wenn ich auf Textelemente stoßen könnte:

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

Soweit ich firstChilddas beurteilen kann, verwendet die NodeList von childNodesund firstElementChildverwendet children. Ich stütze diese Annahme auf die MDN-Referenz:

childNodeist eine Referenz auf das erste untergeordnete Element des Elementknotens oder nullwenn es keines gibt.

Ich vermute, dass in Bezug auf die Geschwindigkeit der Unterschied, wenn überhaupt, so gut wie nichts sein wird, da er firstElementChildeffektiv eine Referenz children[0]ist und das childrenObjekt ohnehin bereits im Speicher ist.

Was mich wirft, ist das childNodesObjekt. Ich habe es verwendet, um ein Formular in einem Tabellenelement zu betrachten. Während childrenalle Formularelemente aufgelistet werden, childNodesscheint es auch Leerzeichen aus dem HTML-Code zu geben:

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

Beide protokollieren <TextNode textContent="\n ">

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

Alle protokollieren <input type="text">. Woher? Ich würde verstehen, dass ein Objekt es mir ermöglichen würde, mit dem „rohen“ HTML-Code zu arbeiten, während das andere beim DOM bleibt, aber das childNodesElement scheint auf beiden Ebenen zu funktionieren.

Um auf meine ursprüngliche Frage zurückzukommen, würde ich raten: Wenn ich das umfassendste Objekt haben möchte, childNodesist dies der richtige Weg, aber aufgrund seiner Vollständigkeit ist es möglicherweise nicht das vorhersehbarste, wenn es das gewünschte Element zurückgibt. zu jedem Zeitpunkt erwarten. Die browserübergreifende Unterstützung könnte sich in diesem Fall ebenfalls als Herausforderung erweisen, obwohl ich mich irren könnte.

Könnte jemand die Unterscheidung zwischen den vorliegenden Objekten klarstellen? Wenn es einen Geschwindigkeitsunterschied gibt, wie vernachlässigbar er auch sein mag, würde ich gerne wissen. Wenn ich das alles falsch sehe, kannst du mich gerne erziehen.


PS: Bitte, bitte, ich mag JavaScript, also ja, ich möchte mich mit so etwas befassen. Antworten wie „jQuery kümmert sich für Sie darum“ sind nicht das, wonach ich suche, daher nein Etikett.

Elias Van Ootegem
quelle
49
>> bitte, bitte, ich mag JavaScript, also ja, ich möchte mich mit so etwas befassen. Antworten wie "jQuery befasst sich damit für Sie" sind nicht das, wonach ich suche. << upvotes for this
david.barkhuizen

Antworten:

211

Klingt so, als würden Sie es überdenken. Sie haben den Unterschied zwischen childNodesund beobachtet children, der childNodesalle Knoten enthält, einschließlich Textknoten, die vollständig aus Leerzeichen bestehen, während childrenes sich nur um eine Sammlung der untergeordneten Knoten handelt, die Elemente sind. Das ist wirklich alles.

An beiden Sammlungen ist nichts Unvorhersehbares, obwohl einige Punkte zu beachten sind:

  • IE <= 8 enthält keine Nur-Leerzeichen-Textknoten, childNodeswährend andere Browser dies tun
  • IE <= 8 enthält Kommentarknoten, childrenwährend andere Browser nur Elemente enthalten

children, firstElementChildUnd Freunde sind nur Komfort, eine gefilterte Ansicht des DOM beschränkt auf nur Elemente präsentieren.

Tim Down
quelle
Das ist auch so, obwohl mich eines immer noch stört: Der Whitespace-Textknoten, den childNodes aufgreift, ist eigentlich nur eine neue Zeile und ein paar Tabulatoren im Code. Liegt das am XHTML-Doctype? Kann dies behoben werden, indem das Leerzeichen in den Strukturcode innerhalb der Tags eingeschlossen wird?
Elias Van Ootegem
@EliasVanOotegem: Das hat nichts mit doctype zu tun. Leerraumtextknoten sind immer im DOM enthalten (außer in IE <9), sowohl für HTML als auch für XHTML, wo immer sie in der Quelle erscheinen. Es ist im Allgemeinen eine gute Sache, obwohl es Sie auffangen kann, wenn Sie nicht darauf vorbereitet sind.
Tim Down
Ich meinte, wenn es helfen würde, mein Markup so zu schreiben <td\n\t>content</td>. Wie bei XML, um zu vermeiden, dass überschüssiges Leerzeichen als Teil der Daten (oder in diesem Fall als DOM) betrachtet wird. Warum sollten Leerzeichen in einem Tag in das DOM aufgenommen werden?
Elias Van Ootegem
@ EliasVanOotegem: Oh, ich verstehe. Leerzeichen nach dem Tag-Namen in einem Tag werden ignoriert, sodass Sie so etwas tun können. Ich habe diese Technik in präzisen Layouts verwendet, um zu verhindern, dass Browser winzige Lücken für Leerzeichen zwischen einigen Arten von Elementen hinzufügen.
Tim Down
2
@Christophe: Ah, fairer Punkt, danke. Ich werde meiner Antwort eine Notiz hinzufügen. Angesichts der Tatsache, dass childrenIE 4 über ein Jahrzehnt entstanden ist, bevor es zum Standard wurde oder von anderen Browsern übernommen wurde, könnte man argumentieren, dass IE <= 8 korrekt und andere Browser falsch sind.
Tim Down
23

firstElementChild ist möglicherweise nicht in IE <9 verfügbar (nur firstChild)

on IE <9 firstChild ist das firstElementChild, da MS DOM (IE <9) keine leeren Textknoten speichert. Wenn Sie dies jedoch in anderen Browsern tun, werden leere Textknoten zurückgegeben ...

meine Lösung

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

Dies gibt das erste Kind sogar auf IE <9

neu-rah
quelle
Wenn elem.firstChildes sich nicht um einen Elementknoten handelt, sind die Ergebnisse in alten IEs unterschiedlich, da elem.firstElementChildes sich immer um einen Elementknoten handelt.
Duri
Es tut mir leid, aber ich weiß, wie ich das erste Kind in allen gängigen Browsern bekomme. Ich möchte wissen, wie die verschiedenen Objekte strukturiert sind, um die Ähnlichkeiten und Unterschiede zwischen ihnen besser zu verstehen. Ich werde meine Frage später bearbeiten, um das klarer zu machen. Entschuldigen Sie die Verwechslung
Elias Van Ootegem
1
firstElementChildist auch in Nicht-IE-Browsern nicht unbedingt sicher: Firefox unterstützt es erst in Version 3.5.
Tim Down
Dies funktioniert für Chrome, IE9 und IE8, da die andere Prüfung nicht ausgeführt wird, wenn sie das firstElementChild haben. Andernfalls (alter IE) haben wir das firstChild und es ist ein Elementknoten für Browser, der kein firstElementChild hat (IE <9) ) ... immer noch frage ich mich über FF
neu-rah
1
Tim ist richtig, wenn ich an diese Sachen googeln Objekte dieses auftauchten, gute Seite einmal zu überprüfen , in eine Zeit lang
Elias Van Ootegem
20

Die Cross - Browser - Art und Weise zu tun ist , zu verwenden , childNodeszu erhalten NodeList, dann einen Array aller Knoten mit machen 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/

Dies ist besonders einfach, wenn Sie eine Dienstprogrammbibliothek wie lodash verwenden :

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

Zukunft:

Sie können querySelectorAllin Kombination mit einer :scopePseudoklasse verwenden (entspricht dem Element, das der Referenzpunkt des Selektors ist):

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

Zum Zeitpunkt des Schreibens :scopewird dies in Chrome, Firefox und Safari unterstützt.

Gajus
quelle
: Umfang wurde aus der Spezifikation entfernt . Sollten wir den Abschnitt Zukunft entfernen ?
Thomas Marti
4

Um die anderen Antworten zu ergänzen, gibt es hier immer noch bemerkenswerte Unterschiede, insbesondere beim Umgang mit <svg>Elementen.

Ich habe beides benutzt .childNodesund .childrenes vorgezogen, mit dem HTMLCollectionvom .childrenGetter gelieferten zu arbeiten .

Heute stieß ich jedoch auf Probleme mit IE / Edge-Fehlern bei der Verwendung .childrenauf einem <svg>. Während .childrenes im IE für grundlegende HTML-Elemente unterstützt wird, wird es für Dokumente / Dokumentfragmente oder SVG-Elemente nicht unterstützt .

Für mich war es möglich, einfach die benötigten Elemente über zu erfassen, .childNodes[n]da ich keine überflüssigen Textknoten habe, über die ich mir Sorgen machen muss. Möglicherweise können Sie dasselbe tun, aber wie oben an anderer Stelle erwähnt, vergessen Sie nicht, dass Sie möglicherweise auf unerwartete Elemente stoßen.

.childrenIch hoffe, dies ist hilfreich für jemanden, der sich am Kopf kratzt und versucht herauszufinden, warum es an anderer Stelle in seinem js im modernen IE funktioniert und bei Dokumenten- oder SVG-Elementen fehlschlägt.

slothluvchunk
quelle
3

Hey Leute, lasst euch nicht vom Leerraum täuschen. Testen Sie dies einfach in einem Konsolenbrowser. Verwenden Sie natives Javascript. Hier ist ein Beispiel mit zwei 'ul'-Mengen mit derselben Klasse. Sie müssen Ihre 'ul'-Liste nicht in einer Zeile haben, um Leerzeichen zu vermeiden. Verwenden Sie einfach Ihre Array-Anzahl, um über Leerzeichen zu springen.

Wie um Leerraum zu bekommen , querySelector()dann childNodes[]Geige Link js: 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 Paradies
quelle
Nicht meine Ablehnung, aber ich denke, jemand hat abgelehnt, weil: a) Diese Frage ist 4 Jahre alt und document.querySelectorwurde damals nicht gut unterstützt. b) Die Frage ist im Grunde, was die Unterschiede zwischen childrenund childNodes intern sind , was zuverlässiger ist, wenn man sich voreinander entscheidet. und c) Da, wie in der Frage demonstraded: childNodes nicht enthalten Leerzeichen Knoten, childrennicht ...
Elias Van Ootegem