Aktualizacja :
Przedstawiłem kilka podstawowych testów wydajności dla każdej z tych 6 metod na 1000 uruchomień. getElementsByTagName
jest najszybszy, ale wykonuje półdupą robotę, ponieważ nie wybiera wszystkich elementów, a tylko jeden określony typ tagu (tak mi się wydaje p
) i ślepo zakłada, że jego firstChild jest elementem tekstowym. Może być trochę wadliwy, ale służy do celów demonstracyjnych i porównania jego wydajności z TreeWalker
. Przeprowadź testy na jsfiddle, aby zobaczyć wyniki.
- Korzystanie z TreeWalker
- Niestandardowe przemierzanie iteracyjne
- Niestandardowe przemierzanie rekurencyjne
- Zapytanie XPath
- querySelectorAll
- getElementsByTagName
Załóżmy na chwilę, że istnieje metoda, która pozwala na Text
natywne pobranie wszystkich węzłów. Nadal musiałbyś przejść przez każdy wynikowy węzeł tekstowy i wywołać, node.nodeValue
aby uzyskać rzeczywisty tekst, tak jak w przypadku każdego węzła DOM. Zatem kwestia wydajności nie polega na iterowaniu przez węzły tekstowe, ale na iterowaniu przez wszystkie węzły, które nie są tekstowe i sprawdzaniu ich typu. Twierdziłbym (na podstawie wyników), że TreeWalker
działa tak samo szybko getElementsByTagName
, jeśli nie szybciej (nawet z handicapped getElementsByTagName).
Uruchomiono każdy test 1000 razy.
Metoda Suma ms Średnia ms
--------------------------------------------------
document.TreeWalker 301 0,301
Iteracyjny Traverser 769 0,769
Rekurencyjny Traverser 7352 7.352
Zapytanie XPath 1849 1,849
querySelectorAll 1725 1.725.0
getElementsByTagName 212 0.212
Źródło dla każdej metody:
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);
}
}
Rekurencyjne przechodzenie po drzewie
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);
}
Iteracyjne przechodzenie po drzewie
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 (handicap)
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);
}
}
Również ta dyskusja może być pomocna - http://bytes.com/topic/javascript/answers/153239-how-do-i-get-elements-text-node
node.nodeType = 3
nanode.nodeType == 3
=
błędu. Naprawiłem to, a wersja xpath po prostu zwracałaText
obiekty, a nie rzeczywisty ciąg znaków w nim zawarty, tak jak robiły to inne metody. Metoda polegająca na pobieraniu tylko tekstu pierwszego dziecka jest celowo błędna, o czym wspomniałem na początku. Ponownie przeprowadzę testy i opublikuję zaktualizowane wyniki tutaj. Wszystkie testy (z wyjątkiem getElementsByTagName i xpath) zwracają taką samą liczbę węzłów tekstowych. XPath zgłasza około 20 węzłów więcej niż inne, które na razie zignoruję.Oto nowoczesna
Iterator
wersja najszybszej metody TreeWalker: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; }
Stosowanie:
for (const textNode of getTextNodesIterator(document.body)) { console.log(textNode) }
Bezpieczniejsza wersja
Bezpośrednie użycie iteratora może utknąć, jeśli przesuniesz węzły podczas wykonywania pętli. To jest bezpieczniejsze, zwraca tablicę:
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; }
źródło
Wiem, że specjalnie poprosiłeś o kolekcję, ale jeśli miałeś na myśli to nieformalnie i nie obchodziło Cię, czy wszystkie zostały połączone w jeden duży sznurek, możesz użyć:
var allTextAsString = document.documentElement.textContent || document.documentElement.innerText;
... z pierwszym elementem jest podejście standardowe DOM3. Zwróć jednak uwagę, że
innerText
wydaje się, że wyklucza zawartość skryptu lub tagu stylu w implementacjach, które je obsługują (przynajmniej IE i Chrome), podczas gdytextContent
je zawiera (w przeglądarkach Firefox i Chrome).źródło
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; }
/ * Możesz zwrócić tablicę wszystkich podrzędnych węzłów tekstowych jakiegoś elementu nadrzędnego lub możesz przekazać mu jakąś funkcję i zrobić coś (znaleźć, zamienić lub cokolwiek) z tekstem w miejscu.
Ten przykład zwraca tekst węzłów tekstowych niebędących białymi znakami w treści:
var A= document.deepText(document.body, function(t){ var tem= t.data; return /\S/.test(tem)? tem: undefined; }); alert(A.join('\n'))
* /
Przydatny do wyszukiwania i zastępowania, podświetlania i tak dalej
źródło
Oto alternatywa, która jest nieco bardziej idiomatyczna i (miejmy nadzieję) łatwiejsza do zrozumienia.
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 } } }
źródło
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)
źródło
po
createTreeWalker
jest przestarzałe, możesz użyć/** * 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; }
źródło