Il modo migliore per ottenere nodi figlio


233

Mi chiedevo, JavaScript offre una varietà di metodi per ottenere il primo elemento figlio da qualsiasi elemento, ma qual è il migliore? Per meglio, intendo: la maggior parte compatibile con più browser, più veloce, più completa e prevedibile quando si tratta di comportamento. Un elenco di metodi / proprietà che utilizzo come alias:

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

Questo funziona per entrambi i casi:

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

Questo è in caso di forme o <div>iterazioni. Se potessi incontrare elementi di testo:

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

Per quanto ne so, firstChildusa la NodeList childNodese firstElementChildusa children. Sto basando questo presupposto sul riferimento MDN:

childNodeè un riferimento al primo elemento figlio del nodo elemento, o nullse non ce n'è uno.

Immagino che, in termini di velocità, la differenza, se presente, non sarà quasi nulla, dal momento che firstElementChildè effettivamente un riferimento a children[0], e l' childrenoggetto è già in memoria comunque.

Ciò che mi lancia è l' childNodesoggetto. L'ho usato per dare un'occhiata a un modulo, in un elemento tabella. Mentre childrenelenca tutti gli elementi del modulo, childNodessembra includere anche gli spazi bianchi dal codice HTML:

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

Entrambi i registri <TextNode textContent="\n ">

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

Tutti i log <input type="text"... >. Come mai? Comprenderei che un oggetto mi consentirebbe di lavorare con il codice HTML "grezzo", mentre l'altro si attacca al DOM, ma l' childNodeselemento sembra funzionare su entrambi i livelli.

Per tornare alla mia domanda iniziale, la mia ipotesi sarebbe: se voglio l'oggetto più completo, childNodesè la strada da percorrere, ma a causa della sua completezza, potrebbe non essere il più prevedibile in termini di restituzione dell'elemento che voglio / aspettarsi in qualsiasi momento. Il supporto cross-browser potrebbe anche rivelarsi una sfida in quel caso, anche se potrei sbagliarmi.

Qualcuno potrebbe chiarire la distinzione tra gli oggetti a portata di mano? Se c'è una differenza di velocità, per quanto trascurabile, vorrei saperlo anche io. Se vedo tutto sbagliato, sentiti libero di educarmi.


PS: Per favore, per favore, mi piace JavaScript, quindi sì, voglio occuparmi di questo genere di cose. Risposte come "jQuery si occupa di questo per te" non sono quello che sto cercando, quindi no etichetta.


50
>> per favore, per favore, mi piace JavaScript, quindi sì, voglio occuparmi di questo genere di cose. risposte come 'jQuery si occupa di questo per te' non sono quello che sto cercando << voti positivi per questo
david.barkhuizen,

Risposte:


211

Sembra che tu ci stia pensando troppo. Hai osservato la differenza tra childNodese children, che childNodescontiene tutti i nodi, inclusi i nodi di testo costituiti interamente da spazi bianchi, mentre childrenè una raccolta di soli nodi figlio che sono elementi. Questo è davvero tutto ciò che c'è da fare.

Non c'è nulla di imprevedibile in entrambe le raccolte, anche se ci sono un paio di problemi da tenere presente:

  • IE <= 8 non include nodi di testo solo spazi vuoti childNodesmentre altri browser lo fanno
  • IE <= 8 include nodi di commento all'interno childrenmentre altri browser hanno solo elementi

children, firstElementChildE gli amici sono solo comodità, che presentano una visualizzazione filtrata del DOM limitato a pochi elementi.


Immaginato, anche se una cosa mi dà ancora fastidio: il textnode degli spazi bianchi su cui childNodes raccoglie in realtà è solo una nuova riga e un paio di schede nel codice. Ciò è dovuto al doctype XHTML? questo può essere risolto racchiudendo lo spazio bianco per strutturare il codice all'interno dei tag?
Elias Van Ootegem,

@EliasVanOotegem: non è correlato a doctype. I nodi di testo con spazi vuoti sono sempre inclusi nel DOM (tranne in IE <9) sia per HTML che per XHTML, ovunque appaiano nell'origine. In genere è una buona cosa, anche se può sorprenderti se non sei preparato per questo.
Tim Down

Intendevo dire se sarebbe stato utile scrivere il mio markup in questo modo <td\n\t>content</td>. Come è possibile fare con XML, per evitare che gli spazi bianchi in eccesso vengano considerati parte dei dati (o DOM, in questo caso). Perché gli spazi bianchi all'interno di un tag dovrebbero essere inclusi nel DOM?
Elias Van Ootegem,

@EliasVanOotegem: Oh, capisco. Lo spazio bianco dopo il nome del tag all'interno di un tag viene ignorato, quindi è possibile fare questo tipo di cose. Ho visto quella tecnica utilizzata in layout precisi per impedire ai browser di aggiungere piccoli spazi vuoti per lo spazio bianco tra alcuni tipi di elementi.
Tim Down

2
@Christophe: Ah, punto giusto, grazie. Aggiungerò una nota alla mia risposta. Dato che ha childrenavuto origine in IE 4 oltre un decennio prima di diventare uno standard o essere adottato da altri browser, si potrebbe sostenere che IE <= 8 sia corretto e che altri browser siano sbagliati.
Tim Down,

23

firstElementChild potrebbe non essere disponibile in IE <9 (solo firstChild)

su IE <9 firstChild è il firstElementChild perché MS DOM (IE <9) non memorizza nodi di testo vuoti. Ma se lo fai su altri browser restituiranno nodi di testo vuoti ...

la mia soluzione

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

questo darà al primo figlio anche su IE <9


Se elem.firstChildnon è un nodo elemento, i risultati saranno diversi nei vecchi IE, perché elem.firstElementChildè sempre nodo elemento.
duri

Mi dispiace, ma so come ottenere il primo figlio in tutti i principali browser. Quello che voglio sapere è come sono strutturati i diversi oggetti, al fine di comprendere meglio le somiglianze e le differenze tra loro.
Modificherò la

1
firstElementChildnon è necessariamente sicuro nei browser non IE: Firefox ha iniziato a supportarlo solo nella versione 3.5.
Tim Down,

questo funziona per Chrome, IE9 e IE8 perché se hanno il firstElementChild, l'altro controllo non viene eseguito, altrimenti (vecchio IE) abbiamo il firstChild, ed è un nodo elemento per browser che non ha firstElementChild (IE <9 ) ... mi chiedo ancora di FF
neu-rah,

1
Tim ha ragione, quando cercavo su Google cose su questi oggetti, questo si presentava, un buon sito da controllare una volta ogni tanto
Elias Van Ootegem

20

Il modo cross browser da fare è utilizzare childNodesper ottenere NodeList, quindi creare un array di tutti i nodi con 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/

Ciò è particolarmente semplice se si utilizza una libreria di utilità come lodash :

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

Futuro:

È possibile utilizzare querySelectorAllin combinazione con la :scopepseudo-classe (corrisponde all'elemento che rappresenta il punto di riferimento del selettore):

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

Al momento della stesura di questo :scopeè supportato in Chrome, Firefox e Safari.


: l'ambito è stato rimosso dalle specifiche . Dovremmo rimuovere la sezione Future ?
Thomas Marti,

4

Solo per aggiungere alle altre risposte, ci sono ancora differenze notevoli qui, in particolare quando si tratta di <svg>elementi.

Ho usato entrambi .childNodese .childrenho preferito lavorare con il HTMLCollectionconsegnato dal .childrengetter.

Oggi, tuttavia, ho riscontrato problemi con IE / Edge non riusciti durante l'utilizzo .childrensu un <svg>. Sebbene .childrensia supportato in IE su elementi HTML di base, non è supportato su frammenti di documento / documento o elementi SVG .

Per me, sono stato in grado di afferrare semplicemente gli elementi necessari .childNodes[n]perché non ho nodi di testo estranei di cui preoccuparmi. Potresti essere in grado di fare lo stesso, ma come menzionato altrove sopra, non dimenticare che potresti imbatterti in elementi inaspettati.

Spero che questo sia utile per qualcuno che si gratta la testa cercando di capire perché .childrenfunziona altrove nei loro js su IE moderno e non riesce su documenti o elementi SVG.


3

Ehi ragazzi non lasciatevi ingannare dagli spazi bianchi. prova questo in un browser console. usa javascript nativo. Ecco un esempio con due set 'ul' con la stessa classe. Non è necessario avere l'elenco 'ul' tutto in una riga per evitare lo spazio bianco, basta usare il conteggio dell'array per saltare sullo spazio bianco.

Come ottenere spazio intorno bianco con querySelector()allora childNodes[]JS collegamento violino: 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>

Non il mio voto negativo, ma penso che qualcuno abbia votato in negativo perché: a) Questa domanda ha 4 anni, allora document.querySelectornon era ben supportata. b) La domanda è in sostanza porre quali sono le differenze tra childrene childNodes internamente , il che è più affidabile, quando scegliere l'una rispetto all'altra. c) Perché, come demonstraded nella domanda: childNodes non includono gli spazi nodi, childrennon fa ...
Elias Van Ootegem
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.