Meilleure façon d'obtenir des nœuds enfants

233

Je me demandais, JavaScript propose une variété de méthodes pour obtenir le premier élément enfant de n'importe quel élément, mais quelle est la meilleure? Au mieux, je veux dire: le plus compatible avec tous les navigateurs, le plus rapide, le plus complet et le plus prévisible en matière de comportement. Une liste de méthodes / propriétés que j'utilise comme alias:

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

Cela fonctionne pour les deux cas:

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

C'est en cas de formes, ou d' <div>itération. Si je pouvais rencontrer des éléments de texte:

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

Pour autant que je puisse travailler, firstChildutilise la NodeList de childNodes, et firstElementChildutilise children. Je fonde cette hypothèse sur la référence MDN:

childNodeest une référence au premier élément enfant du nœud d'élément, ou nulls'il n'y en a pas.

Je suppose que, en termes de vitesse, la différence, le cas échéant, ne sera presque rien, car il firstElementChilds'agit effectivement d'une référence à children[0], et l' childrenobjet est déjà en mémoire de toute façon.

Ce qui me jette, c'est l' childNodesobjet. Je l'ai utilisé pour jeter un œil à un formulaire, dans un élément de table. Bien qu'il childrenrépertorie tous les éléments du formulaire, childNodesil semble également inclure des espaces dans le code HTML:

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

Les deux journaux <TextNode textContent="\n ">

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

Tous les journaux <input type="text">. Comment venir? Je comprendrais qu'un objet me permettrait de travailler avec le code HTML «brut», tandis que l'autre reste fidèle au DOM, mais l' childNodesélément semble fonctionner aux deux niveaux.

Pour revenir à ma question initiale, je suppose que si je veux l'objet le plus complet, childNodesc'est le chemin à parcourir, mais en raison de son exhaustivité, ce n'est peut-être pas le plus prévisible en termes de retour de l'élément que je veux / attendre à tout moment. La prise en charge de plusieurs navigateurs pourrait également s'avérer être un défi dans ce cas, bien que je puisse me tromper.

Quelqu'un pourrait-il clarifier la distinction entre les objets à portée de main? S'il y a une différence de vitesse, même négligeable, j'aimerais aussi le savoir. Si je vois tout cela de travers, n'hésitez pas à me renseigner.


PS: S'il vous plaît, s'il vous plaît, j'aime JavaScript, alors oui, je veux m'occuper de ce genre de choses. Les réponses comme «jQuery s'occupe de cela pour vous» ne sont pas ce que je recherche, donc non marque.

Elias Van Ootegem
la source
50
>> s'il vous plaît, s'il vous plaît, j'aime JavaScript, alors oui, je veux faire face à ce genre de chose. des réponses comme «jQuery s'occupe de cela pour vous» ne sont pas ce que je recherche << votes positifs pour cela
david.barkhuizen

Réponses:

211

On dirait que vous y pensez trop. Vous avez observé la différence entre childNodeset children, qui childNodescontient tous les nœuds, y compris les nœuds de texte entièrement constitués d'espaces, tandis childrenqu'une collection se compose uniquement des nœuds enfants qui sont des éléments. C'est vraiment tout ce qu'il y a à faire.

Il n'y a rien d'imprévisible dans l'une ou l'autre collection, bien qu'il y ait quelques problèmes à prendre en compte:

  • IE <= 8 n'inclut pas les nœuds de texte à espace blanc uniquement childNodestandis que les autres navigateurs le font
  • IE <= 8 inclut des nœuds de commentaire à l'intérieur childrentandis que les autres navigateurs n'ont que des éléments

children, firstElementChildet les amis ne sont que des commodités, présentant une vue filtrée du DOM restreinte aux seuls éléments.

Tim Down
la source
J'en pense autant, bien qu'une chose me dérange encore: le noeud texte en blanc sur lequel childNodes prend n'est en fait qu'une nouvelle ligne et quelques onglets dans le code. Est-ce dû au doctype XHTML? cela peut-il être résolu en enfermant l'espace blanc dans le code struct dans les balises?
Elias Van Ootegem
@EliasVanOotegem: Cela n'est pas lié au doctype. Les nœuds de texte d'espaces blancs sont toujours inclus dans le DOM (sauf dans IE <9) pour HTML et XHTML, où qu'ils apparaissent dans la source. C'est généralement une bonne chose, bien que cela puisse vous rattraper si vous n'y êtes pas préparé.
Tim Down du
Je voulais dire si cela pouvait aider à écrire mon balisage comme ceci <td\n\t>content</td>. Comme vous pouvez le faire avec XML, pour éviter que les espaces blancs excédentaires ne soient considérés comme faisant partie des données (ou DOM, dans ce cas). Pourquoi les espaces à l'intérieur d'une balise seraient-ils inclus dans le DOM?
Elias Van Ootegem
@EliasVanOotegem: Oh, je vois. L'espace blanc après le nom de la balise à l'intérieur d'une balise est ignoré, vous pouvez donc faire ce genre de chose. J'ai vu cette technique utilisée dans des mises en page précises pour empêcher les navigateurs d'ajouter de minuscules espaces blancs entre certains types d'éléments.
Tim Down du
2
@Christophe: Ah, juste point, merci. J'ajouterai une note à ma réponse. Étant donné que l' childrenorigine dans IE 4 sur une décennie avant de devenir une norme ou d'être adoptée par d'autres navigateurs, vous pourriez affirmer que IE <= 8 est correct et que d'autres navigateurs ont tort.
Tim Down
23

firstElementChild peut ne pas être disponible dans IE <9 (uniquement firstChild)

sur IE <9 firstChild est le firstElementChild car MS DOM (IE <9) ne stocke pas de nœuds de texte vides. Mais si vous le faites sur d'autres navigateurs, ils renverront des nœuds de texte vides ...

ma solution

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

cela donnera le premier enfant même sur IE <9

neu-rah
la source
S'il elem.firstChildne s'agit pas d'un nœud d'élément, les résultats seront différents dans les anciens IE, car il elem.firstElementChilds'agit toujours d'un nœud d'élément.
Duri
Je suis désolé, mais je sais comment obtenir le premier enfant dans tous les principaux navigateurs. Ce que je veux savoir, c'est comment les différents objets sont structurés, afin de mieux comprendre les similitudes et les différences entre eux. Je modifierai ma question plus tard pour que ce soit plus clair, désolé pour le mélange
Elias Van Ootegem
1
firstElementChildn'est pas nécessairement sûr non plus dans les navigateurs non IE: Firefox n'a commencé à le prendre en charge que dans la version 3.5.
Tim Down
cela fonctionne pour chrome, IE9 et IE8 parce que s'ils ont le premierElementChild alors l'autre vérification n'est pas exécutée, sinon (ancien IE) nous avons le firstChild, et c'est un nœud d'élément pour le navigateur qui n'a pas firstElementChild (IE <9 ) ... je me pose encore des questions sur FF
neu-rah
1
Tim a raison, quand je cherchais des trucs sur ces objets, cela s'est révélé, bon site à vérifier de temps en temps
Elias Van Ootegem
20

La façon de naviguer entre les navigateurs consiste à utiliser childNodespour obtenir NodeList, puis à créer un tableau de tous les nœuds avec 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/

C'est particulièrement facile si vous utilisez une bibliothèque d'utilitaires telle que lodash :

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

Futur:

Vous pouvez utiliser querySelectorAllen combinaison avec une :scopepseudo-classe (correspond à l'élément qui est le point de référence du sélecteur):

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

Au moment de la rédaction, cela :scopeest pris en charge dans Chrome, Firefox et Safari.

Gajus
la source
: la portée a été supprimée de la spécification . Faut-il supprimer la section Future ?
Thomas Marti
4

Pour ajouter aux autres réponses, il y a encore des différences notables ici, en particulier en ce qui concerne les <svg>éléments.

J'ai utilisé les deux .childNodeset .childrenj'ai préféré travailler avec le HTMLCollectionlivré par le .childrengetter.

Aujourd'hui cependant, j'ai rencontré des problèmes avec IE / Edge échouant lors de l'utilisation .childrensur un <svg>. Bien qu'il .childrensoit pris en charge dans IE sur les éléments HTML de base, il n'est pas pris en charge sur les fragments de document / document ou les éléments SVG .

Pour moi, j'ai pu simplement saisir les éléments nécessaires via .childNodes[n]car je n'ai pas de nœuds de texte superflus à craindre. Vous pouvez peut-être faire de même, mais comme mentionné ci-dessus, n'oubliez pas que vous pouvez rencontrer des éléments inattendus.

J'espère que cela est utile à quelqu'un qui se gratte la tête en essayant de comprendre pourquoi .childrenfonctionne ailleurs dans son js sur IE moderne et échoue sur les éléments de document ou SVG.

slothluvchunk
la source
3

Hé les gars, ne laissez pas les espaces blancs vous tromper. il suffit de tester cela dans un navigateur de console. utilisez le javascript natif. Voici et exemple avec deux ensembles 'ul' avec la même classe. Vous n'avez pas besoin d'avoir votre liste «ul» sur une seule ligne pour éviter les espaces blancs, utilisez simplement le nombre de tableaux pour sauter par-dessus l'espace blanc.

Comment obtenir de l' espace autour de blanc querySelector()puis childNodes[]js lien violon: 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>
paradis andre
la source
Pas mon vote négatif, mais je pense que quelqu'un a voté contre parce que: a) Cette question a 4 ans, document.querySelectorn'était pas bien soutenue à l'époque. b) La question est essentiellement de savoir quelles sont les différences entre childrenet en childNodes interne , ce qui est plus fiable, quand choisir l'un plutôt que l'autre. et c) Parce que, comme demonstraded dans la question: childNodes ne comprend les espaces nœuds, qui childrenne fonctionne pas ...
Elias Van Ootegem