La mejor manera de obtener nodos secundarios


233

Me preguntaba, JavaScript ofrece una variedad de métodos para obtener el primer elemento hijo de cualquier elemento, pero ¿cuál es el mejor? Por mejor, quiero decir: la mayoría de los navegadores compatibles, más rápido, más completo y predecible cuando se trata de comportamiento. Una lista de métodos / propiedades que uso como alias:

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

Esto funciona para ambos casos:

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

Eso es en caso de formas o <div>iteración. Si pudiera encontrar elementos de texto:

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

Hasta donde puedo trabajar, firstChildusa la NodeList de childNodes, y firstElementChildusa children. Estoy basando esta suposición en la referencia de MDN:

childNodees una referencia al primer elemento hijo del nodo del elemento, o nullsi no hay uno.

Supongo que, en términos de velocidad, la diferencia, si la hay, será casi nula, ya firstElementChildque efectivamente es una referencia children[0]y el childrenobjeto ya está en la memoria de todos modos.

Lo que me arroja es el childNodesobjeto. Lo he usado para echar un vistazo a un formulario, en un elemento de tabla. Si bien childrenenumera todos los elementos del formulario, childNodestambién parece incluir espacios en blanco del código HTML:

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

Ambos log <TextNode textContent="\n ">

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

Todo registro <input type="text"... >. ¿Cómo? Entiendo que un objeto me permitiría trabajar con el código HTML "en bruto", mientras que el otro se adhiere al DOM, pero el childNodeselemento parece funcionar en ambos niveles.

Para volver a mi pregunta inicial, mi suposición sería: si quiero el objeto más completo, childNodeses el camino a seguir, pero debido a su exhaustividad, puede que no sea el más predecible en términos de que devuelva el elemento que quiero / esperar en cualquier momento dado. La compatibilidad entre navegadores también podría ser un desafío en ese caso, aunque podría estar equivocado.

¿Alguien podría aclarar la distinción entre los objetos en cuestión? Si hay una diferencia de velocidad, por insignificante que sea, también me gustaría saberlo. Si veo todo esto mal, no dudes en educarme.


PD: Por favor, por favor, me gusta JavaScript, así que sí, quiero lidiar con este tipo de cosas. Respuestas como "jQuery trata esto para usted" no son lo que estoy buscando, por lo tanto, no etiqueta.


50
>> por favor, por favor, me gusta JavaScript, así que sí, quiero lidiar con este tipo de cosas. respuestas como 'jQuery trata esto para ti' no son lo que estoy buscando << votos a favor para esto
david.barkhuizen

Respuestas:


211

Parece que lo estás pensando demasiado. Ha observado la diferencia entre childNodesy children, que es que childNodescontiene todos los nodos, incluidos los nodos de texto que consisten completamente en espacios en blanco, mientras que childrenes una colección de solo los nodos secundarios que son elementos. Eso es realmente todo lo que hay que hacer.

No hay nada impredecible en ninguna de las colecciones, aunque hay algunos problemas a tener en cuenta:

  • IE <= 8 no incluye nodos de texto de espacio en blanco solo childNodesmientras que otros navegadores sí
  • IE <= 8 incluye nodos de comentarios dentro, childrenmientras que otros navegadores solo tienen elementos

children, firstElementChildy los amigos son solo conveniencias, ya que presentan una vista filtrada del DOM restringida a solo elementos.


Me lo imaginé, aunque una cosa todavía me molesta: el nodo de texto de espacios en blanco que childNodes recoge es en realidad solo una nueva línea y un par de pestañas en el código. ¿Esto se debe al doctype XHTML? ¿Se puede resolver esto encerrando el espacio en blanco para estructurar el código dentro de las etiquetas?
Elias Van Ootegem

@EliasVanOotegem: Eso no está relacionado con doctype. Los nodos de texto de espacios en blanco siempre se incluyen en el DOM (excepto en IE <9) para HTML y XHTML, donde sea que aparezcan en la fuente. En general, es algo bueno, aunque puede atraparte si no estás preparado para ello.
Tim Down

Quise decir si sería útil escribir mi marcado de esta manera <td\n\t>content</td>. Como puede hacer con XML, para evitar que se considere el exceso de espacio en blanco como parte de los datos (o DOM, en este caso). ¿Por qué se incluiría un espacio en blanco dentro de una etiqueta en el DOM?
Elias Van Ootegem

@EliasVanOotegem: Oh, ya veo. Se ignora el espacio en blanco después del nombre de la etiqueta dentro de una etiqueta, por lo que podría hacer ese tipo de cosas. He visto esa técnica utilizada en diseños precisos para evitar que los navegadores agreguen pequeños espacios para espacios en blanco entre algunos tipos de elementos.
Tim Down

2
@ Christopher: Ah, punto justo, gracias. Agregaré una nota a mi respuesta. Dado que se childrenoriginó en IE 4 durante una década antes de convertirse en un estándar o ser adoptado por otros navegadores, podría argumentar que IE <= 8 es correcto y otros navegadores están equivocados.
Tim Down

23

firstElementChild podría no estar disponible en IE <9 (solo firstChild)

en IE <9 firstChild es firstElementChild porque MS DOM (IE <9) no almacena nodos de texto vacíos. Pero si lo hace en otros navegadores, devolverán nodos de texto vacíos ...

mi solución

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

esto le dará al primer hijo incluso en IE <9


Si elem.firstChildno es un nodo de elemento, los resultados serán diferentes en los IE antiguos, porque elem.firstElementChildsiempre es un nodo de elemento.
duri

Lo siento, pero sé cómo obtener el primer hijo en todos los principales navegadores. Lo que quiero saber es cómo se estructuran los diferentes objetos, a fin de comprender mejor las similitudes y diferencias entre ellos.
Editaré

1
firstElementChildtampoco es necesariamente seguro en navegadores que no sean IE: Firefox solo comenzó a admitirlo en la versión 3.5.
Tim Down

esto está funcionando para Chrome, IE9 e IE8 porque si tienen firstElementChild, entonces la otra verificación no se ejecuta, de lo contrario (antiguo IE) tenemos firstChild, y es un nodo de elemento para el navegador que no tiene firstElementChild (IE <9 ) ... todavía me pregunto sobre FF
neu-rah

1
Tim tiene razón, cuando buscaba en Google cosas sobre estos objetos, este apareció, un buen sitio para revisar de vez en cuando
Elias Van Ootegem

20

La forma de hacerlo en el navegador cruzado es usar childNodespara obtener NodeList, luego hacer una matriz de todos los nodos 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/

Esto es especialmente fácil si está utilizando una biblioteca de utilidades como lodash :

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

Futuro:

Puede usar querySelectorAllen combinación con :scopepseudo-clase (coincide con el elemento que es el punto de referencia del selector):

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

Al momento de escribir esto, :scopees compatible con Chrome, Firefox y Safari.


: se eliminó el alcance de la especificación . ¿Deberíamos eliminar la sección Futuro ?
Thomas Marti

4

Solo para agregar a las otras respuestas, todavía hay diferencias notables aquí, específicamente cuando se trata de <svg>elementos.

He usado ambos .childNodesy .childrenhe preferido trabajar con el HTMLCollectionentregado por el .childrengetter.

Hoy, sin embargo, me encontré con problemas con la falla de IE / Edge al usar .childrenen un <svg>. Si bien .childrenes compatible con IE en elementos HTML básicos, no es compatible con documentos / fragmentos de documentos o elementos SVG .

Para mí, pude simplemente tomar los elementos necesarios a través de .childNodes[n]porque no tengo nodos de texto extraños de los que preocuparme. Es posible que pueda hacer lo mismo, pero como se mencionó anteriormente, no olvide que puede encontrarse con elementos inesperados.

Espero que esto sea útil para alguien que se rasca la cabeza tratando de descubrir por qué .childrenfunciona en otra parte de su js en IE moderno y falla en elementos de documento o SVG.


3

Hola chicos, no dejen que el espacio en blanco los engañe. solo prueba esto en un navegador de consola. usa javascript nativo. Aquí hay un ejemplo con dos conjuntos 'ul' con la misma clase. No necesita tener su lista 'ul' en una sola línea para evitar espacios en blanco, solo use su recuento de matriz para saltar sobre el espacio en blanco.

Como llegar espacio alrededor blanco con querySelector()continuación childNodes[]js enlace violín: 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>

No es mi voto negativo, pero creo que alguien votó negativamente porque: a) Esta pregunta tiene 4 años, document.querySelectorno estaba bien respaldada en ese momento. b) La pregunta es básicamente preguntar cuáles son las diferencias internaschildren y childNodes internas , lo cual es más confiable, cuándo elegir una sobre la otra. y c) Porque, como demonstraded en la pregunta: ¿ childNodes Qué incluye espacios en blanco nodos, childrenno ...
Elias Van Ootegem
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.