Espero que esto sea algún tipo de desafío de programación, o tarea asignada. .querySelector()
y .querySelectorAll()
definitivamente harán esto mejor que hacer un equivalente desde cero.
Antes de hablar de código, hablemos de lógica. Estos son los pasos que puede tomar:
- Comenzando en
document.body
,childNodes
loschildNodes
- Si
childNode
es un elemento (porque no todos lo son), agréguelo a una matriz - si el
childNode
es un elemento y tienechildNodes
propios,childNodes
ychildNodes
a una matriz - Filtrar:
- Filtre la matriz a esos nodos que coincidan con su selector CSS
- Solo agregue el nodo si el selector CSS coincide
Cómo abordar el paso 4 depende de usted. Prefiero tareas más pequeñas y dedicadas, así que tomaría la primera opción.
- ¿Necesito agregar un redireccionamiento 301 a la versión 'no www' de mi sitio web para obtener los beneficios de los enlaces que apuntan al sitio 'no www'?
- ¿Cuáles son los mejores libros para Angular 4?
- ¿Cómo funciona una API REST?
- ¿Cuáles son las ventajas y desventajas de trabajar con Wave, la herramienta de seguimiento de programas patentada de McKinsey?
- ¿Cuál sería el costo promedio de diseñar un sitio web de bienes raíces para un agente?
Recorriendo los nodos secundarios
Arrancaría esto creando una función llamada getNodes
función getNodes (elemento) {
var tempArray = [];
for (let child of element.childNodes) {
if (child.nodeType === 1) {
tempArray.push (niño);
}
}
return tempArray;
}
- Creamos una matriz vacía.
- Recorremos la matriz usando ES6’s lovely
for … of
sintaxis - Verificamos y vemos si el nodo es un elemento [1]
- Si es un elemento, lo agregamos a la matriz
Sin embargo, solo una trampa: esto solo va un nivel de profundidad.
Añade algo de recursión
Siendo totalmente honesto, la recursión es difícil para mí. Es una herramienta de programación increíblemente poderosa, pero a veces todavía asusta mi cerebro. La recursión, simplemente, está usando una función para llamarse a sí misma. Y lo que debemos hacer es que getNodes()
llame a sí mismo . Y apuesto a que aquí es donde necesitas ayuda. ¡Así que vamos a ser recursivos!
función getNodes (elemento) {
var tempArray = [];
for (let child of element.childNodes) {
if (child.nodeType === 1) {
tempArray.push (niño);
tempArray.push (getNodes (child)); // recursividad FTW!
}
}
return tempArray;
}
Esto es como helado para mi cerebro; duele pero también se siente tan bien.
Estamos recorriendo estos childNodes
, agregándolos a la matriz, y luego, cuando un childNode
tiene hijos, ¡también los recorremos! todo el camino hacia abajo.
Sin embargo, la única trampa es que esto producirá una matriz multidimensional (una matriz dentro de las matrices). Esos no son fáciles de buscar y filtrar.
Acoplar la matriz
Resulta que la función reduce()
, que está unida al objeto de matriz, puede usarse para aplanar matrices. ¿Pero cómo? ¡Porque también usa la recursividad! Agreguemos una constante cuyo trabajo es aplanar nuestra matriz:
función getNodes (elemento) {
var tempArray = [];
const flatten = arr => arr.reduce ((a, b) => a.concat (Array.isArray (b)? flatten (b): b), []);
for (let child of element.childNodes) {
if (child.nodeType === 1) {
tempArray.push (niño);
tempArray.push (getNodes (child)); // recursividad FTW!
}
}
return flatten (tempArray);
}
Nuestro aplanador está utilizando algunas características sofisticadas de Schmancy ES6:
- Está utilizando una función de flecha
() => {}
oargument => return value
. - reducir es bastante deportivo porque es un acumulador, puede usarlo para combinar un valor actual con un valor siguiente o anterior en una matriz
- a es la acumulación
- b es el valor actual
- A es la concatenación de A y B, pero primero aplanará b
- [] es solo un valor vacío para comenzar la reducción completa
Esto concluye la primera mitad de nuestro desafío. Cuando ejecutamos getNodes(document.body)
, debe obtener con éxito una matriz plana de todos los nodos de elementos en su documento. (pero, por supuesto, puede ejecutar esto en cualquier elemento del DOM).
Filtra la lista
Puede agregar lógica en la función getNodes()
para buscar un selector, pero me gusta la idea de mantener las cosas simples y pequeñas. Así que haré una función cuyo trabajo es tomar un nombre de clase y una matriz, y filtrarlos:
función queryNodes (selector, nodeArray) {
return nodeArray.filter ((nodo) => {
return node.classList.contains (selector);
});
};
Estoy usando el método filter () que vive en el objeto de matriz [2].
- El método filter () acepta una devolución de llamada (y la mía es una función de flecha).
- el método de filtro quiere un valor de retorno que sea booleano (porque eso es lo que le indica si agregarlo a mi nueva lista)
- La función en sí misma devuelve esta lista filtrada
Tenga en cuenta que estoy usando node.classList.contains()
. [3] Estoy trabajando suponiendo que estoy filtrando mi matriz por nombre de clase. Tendría que agregar lógica adicional si quisiera consultar por ID o atributo.
La aplicación final
Ahora que he creado estas dos funciones, debería poder ejecutar esto en el navegador:
var allTheElements = getNodes (document.body);
var justTheChosenOnes = queryNodes (‘elegido’, allTheElements);
Comentarios finales
Use querySelector()
y querySelectorAll()
. No reinventes la rueda.
Notas al pie
[1] Node.nodeType
[2] Array.prototype.filter ()
[3] Element.classList