Actualización :
He descrito algunas pruebas de rendimiento básicas para cada uno de estos 6 métodos en 1000 ejecuciones. getElementsByTagName
es el más rápido, pero hace un trabajo a medias, ya que no selecciona todos los elementos, sino solo un tipo particular de etiqueta (creo p
) y asume ciegamente que su firstChild es un elemento de texto. Puede que tenga algunos defectos, pero está ahí para fines de demostración y para comparar su rendimiento con TreeWalker
. Ejecute las pruebas usted mismo en jsfiddle para ver los resultados.
- Usando un TreeWalker
- Recorrido iterativo personalizado
- Recorrido recursivo personalizado
- Consulta de XPath
- querySelectorAll
- getElementsByTagName
Supongamos por un momento que existe un método que le permite obtener todos los Text
nodos de forma nativa. Aún tendría que atravesar cada nodo de texto resultante y llamar node.nodeValue
para obtener el texto real como lo haría con cualquier nodo DOM. Entonces, el problema del rendimiento no es iterar a través de los nodos de texto, sino a través de todos los nodos que no son texto y verificar su tipo. Yo diría (basado en los resultados) que TreeWalker
funciona tan rápido como getElementsByTagName
, si no más rápido (incluso con getElementsByTagName jugando con discapacidad).
Realicé cada prueba 1000 veces.
Método Total ms Promedio ms
--------------------------------------------------
document.TreeWalker 301 0.301
Recorrido iterativo 769 0,769
Traverser recursivo 7352 7.352
Consulta XPath 1849 1.849
querySelectorAll 1725 1.725
getElementsByTagName 212 0.212
Fuente para cada método:
TreeWalker
function nativeTreeWalker() {
var walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
null,
false
);
var node;
var textNodes = [];
while(node = walker.nextNode()) {
textNodes.push(node.nodeValue);
}
}
Recorrido de árbol recursivo
function customRecursiveTreeWalker() {
var result = [];
(function findTextNodes(current) {
for(var i = 0; i < current.childNodes.length; i++) {
var child = current.childNodes[i];
if(child.nodeType == 3) {
result.push(child.nodeValue);
}
else {
findTextNodes(child);
}
}
})(document.body);
}
Recorrido iterativo del árbol
function customIterativeTreeWalker() {
var result = [];
var root = document.body;
var node = root.childNodes[0];
while(node != null) {
if(node.nodeType == 3) {
result.push(node.nodeValue);
}
if(node.hasChildNodes()) {
node = node.firstChild;
}
else {
while(node.nextSibling == null && node != root) {
node = node.parentNode;
}
node = node.nextSibling;
}
}
}
querySelectorAll
function nativeSelector() {
var elements = document.querySelectorAll("body, body *");
var results = [];
var child;
for(var i = 0; i < elements.length; i++) {
child = elements[i].childNodes[0];
if(elements[i].hasChildNodes() && child.nodeType == 3) {
results.push(child.nodeValue);
}
}
}
getElementsByTagName (desventaja)
function getElementsByTagName() {
var elements = document.getElementsByTagName("p");
var results = [];
for(var i = 0; i < elements.length; i++) {
results.push(elements[i].childNodes[0].nodeValue);
}
}
XPath
function xpathSelector() {
var xpathResult = document.evaluate(
"//*/text()",
document,
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
null
);
var results = [], res;
while(res = xpathResult.iterateNext()) {
results.push(res.nodeValue);
}
}
Además, puede encontrar útil esta discusión: http://bytes.com/topic/javascript/answers/153239-how-do-i-get-elements-text-node
node.nodeType = 3
anode.nodeType == 3
=
error evidente . Lo arreglé, y la versión de xpath simplemente devolvíaText
objetos, y no la cadena real contenida en ella como lo estaban haciendo los otros métodos. El método que solo obtiene el texto del primer hijo es intencionalmente incorrecto, y lo mencioné al principio. Volveré a ejecutar las pruebas y publicaré los resultados actualizados aquí. Todas las pruebas (excepto getElementsByTagName y xpath) devuelven el mismo número de nodos de texto. XPath informa aproximadamente 20 nodos más que los demás, lo que ignoraré por ahora.Aquí hay una
Iterator
versión moderna del método TreeWalker más rápido:function getTextNodesIterator(el) { // Returns an iterable TreeWalker const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT); walker[Symbol.iterator] = () => ({ next() { const value = walker.nextNode(); return {value, done: !value}; } }); return walker; }
Uso:
for (const textNode of getTextNodesIterator(document.body)) { console.log(textNode) }
Versión más segura
El uso del iterador directamente puede atascarse si mueve los nodos mientras realiza un bucle. Esto es más seguro, devuelve una matriz:
function getTextNodes(el) { // Returns an array of Text nodes const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT); const nodes = []; while (walker.nextNode()) { nodes.push(walker.currentNode); } return nodes; }
fuente
Sé que solicitó específicamente una colección, pero si lo decía de manera informal y no le importaba si estaban todos unidos en una cadena grande, puede usar:
var allTextAsString = document.documentElement.textContent || document.documentElement.innerText;
... siendo el primer elemento el enfoque estándar DOM3. Sin embargo, tenga en cuenta que
innerText
parece excluir el contenido de la etiqueta de estilo o script en implementaciones que lo admiten (al menos IE y Chrome) mientras que lostextContent
incluye (en Firefox y Chrome).fuente
document.deepText= function(hoo, fun){ var A= [], tem; if(hoo){ hoo= hoo.firstChild; while(hoo!= null){ if(hoo.nodeType== 3){ if(typeof fun== 'function'){ tem= fun(hoo); if(tem!= undefined) A[A.length]= tem; } else A[A.length]= hoo; } else A= A.concat(document.deepText(hoo, fun)); hoo= hoo.nextSibling; } } return A; }
/ * Puede devolver una matriz de todos los nodos de texto descendientes de algún elemento padre, o puede pasarle alguna función y hacer algo (buscar o reemplazar o lo que sea) con el texto en su lugar.
Este ejemplo devuelve el texto de los nodos de texto que no son espacios en blanco en el cuerpo:
var A= document.deepText(document.body, function(t){ var tem= t.data; return /\S/.test(tem)? tem: undefined; }); alert(A.join('\n'))
* /
Útil para buscar y reemplazar, resaltar, etc.
fuente
Aquí hay una alternativa que es un poco más idiomática y (con suerte) más fácil de entender.
function getText(node) { // recurse into each child node if (node.hasChildNodes()) { node.childNodes.forEach(getText); } // get content of each non-empty text node else if (node.nodeType === Node.TEXT_NODE) { const text = node.textContent.trim(); if (text) { console.log(text); // do something } } }
fuente
var el1 = document.childNodes[0] function get(node,ob) { ob = ob || {}; if(node.childElementCount) { ob[node.nodeName] = {} ob[node.nodeName]["text"] = []; for(var x = 0; x < node.childNodes.length;x++) { if(node.childNodes[x].nodeType == 3) { var txt = node.childNodes[x].nodeValue; ob[node.nodeName]["text"].push(txt) continue } get(node.childNodes[x],ob[node.nodeName]) }; } else { ob[node.nodeName] = (node.childNodes[0] == undefined ? null :node.childNodes[0].nodeValue ) } return ob } var o = get(el1) console.log(o)
fuente
después de que
createTreeWalker
esté en desuso, puede usar/** * Get all text nodes under an element * @param {!Element} el * @return {Array<!Node>} */ function getTextNodes(el) { const iterator = document.createNodeIterator(el, NodeFilter.SHOW_TEXT); const textNodes = []; let currentTextNode; while ((currentTextNode = iterator.nextNode())) { textNodes.push(currentTextNode); } return textNodes; }
fuente