getElementsByTagName () эквивалент для textNodes - PullRequest
68 голосов
/ 05 апреля 2010

Есть ли способ получить коллекцию всех textNode объектов в документе?

getElementsByTagName() прекрасно работает для Элементов, но textNode s не являются Элементами.

Обновление: Я понимаю, что этого можно достичь, пройдя DOM - как предлагают многие ниже. Я знаю, как написать функцию DOM-Walker, которая просматривает каждый узел в документе. Я надеялся, что есть какой-нибудь браузерный способ сделать это. В конце концов, немного странно, что я могу получить все <input> s за один встроенный вызов, но не все textNode s.

Ответы [ 6 ]

101 голосов
/ 05 апреля 2010

Обновление

Я изложил некоторые базовые тесты производительности для каждого из этих 6 методов за 1000 прогонов. getElementsByTagName является самым быстрым, но он выполняет половинную работу, поскольку он выбирает не все элементы, а только один конкретный тип тега (я думаю p) и слепо предполагает, что его firstChild является текстовым элементом. Это может быть немного некорректно, но оно есть для демонстрационных целей и сравнения его производительности с TreeWalker. Запустите тесты на jsfiddle , чтобы увидеть результаты.

  1. Использование TreeWalker
  2. Настраиваемый итеративный обход
  3. Пользовательский рекурсивный обход
  4. запрос Xpath
  5. querySelectorAll
  6. getElementsByTagName

Давайте на минутку предположим, что есть метод, который позволяет вам получить все Text узлы изначально. Вам все равно придется пройти через каждый результирующий текстовый узел и вызвать node.nodeValue, чтобы получить реальный текст, как если бы вы работали с любым узлом DOM. Таким образом, проблема производительности заключается не в итерации по текстовым узлам, а в прохождении всех узлов, которые не являются текстовыми, и проверке их типа. Я бы сказал (основываясь на результатах), что TreeWalker работает так же быстро, как getElementsByTagName, если не быстрее (даже если getElementsByTagName играет с ограниченными возможностями).

Ran each test 1000 times.

Method                  Total ms        Average ms
--------------------------------------------------
document.TreeWalker          301            0.301
Iterative Traverser          769            0.769
Recursive Traverser         7352            7.352
XPath query                 1849            1.849
querySelectorAll            1725            1.725
getElementsByTagName         212            0.212
<ч />

Источник для каждого метода:

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);
    }
}

Рекурсивный обход деревьев

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);
}

Итеративный обход дерева

function customIterativeTreeWalker() {
    var result = [];
    var root = document.body;

    var node = root.childNodes[0];
    while(node != null) {
        if(node.nodeType == 3) { /* Fixed a bug here. Thanks @theazureshadow */
            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 *"); /* Fixed a bug here. Thanks @theazureshadow */
    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 (гандикап)

function getElementsByTagName() {
    var elements = document.getElementsByTagName("p");
    var results = [];
    for(var i = 0; i < elements.length; i++) {
        results.push(elements[i].childNodes[0].nodeValue);
    }
}

* 1061 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);  /* Fixed a bug here. Thanks @theazureshadow */
    }
}

Также вам может пригодиться это обсуждение - http://bytes.com/topic/javascript/answers/153239-how-do-i-get-elements-text-node

4 голосов
/ 01 апреля 2011

Я знаю, что вы специально просили коллекцию, но если вы просто имели в виду это неофициально и не заботились о том, чтобы все они были объединены в одну большую строку, вы можете использовать:

var allTextAsString = document.documentElement.textContent || document.documentElement.innerText;

... с первым пунктом, являющимся стандартным подходом DOM3. Однако обратите внимание, что innerText, по-видимому, исключает содержимое тегов скрипта или стиля в реализациях, которые его поддерживают (по крайней мере, IE и Chrome), в то время как textContent включает их (в Firefox и Chrome).

2 голосов
/ 13 июня 2017

Вот современная Iterator версия самого быстрого метода TreeWalker:

function getTextNodesIterator(el) { // Returns an iterable TreeWalker
    const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
    const next = () => {
        const value = walker.nextNode();
        return {
            value,
            done: !value
        };
    };
    walker[Symbol.iterator] = () => ({next});
    return walker;
}

Использование:

for (const textNode of getTextNodesIterator(document.body)) {
    console.log(textNode)
}

Но цикл может застрять, если вы перемещаете узлы во время цикла.

Это безопаснее:

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;
}
1 голос
/ 09 марта 2019

Вот альтернатива, которая немного более идиоматична и (надеюсь) более понятна.

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
        }
    }
}
1 голос
/ 05 апреля 2010
 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;
    }

/ * Вы можете вернуть массив всех дочерних текстовых узлов некоторого родительского элемента, или вы можете передать ему некоторую функцию и сделать что-то (найти или заменить или что-то еще) к тексту на месте.

В этом примере возвращается текст текстовых узлов без пробелов в теле:

var A= document.deepText(document.body, function(t){
    var tem= t.data;
    return /\S/.test(tem)? tem: undefined;
});
alert(A.join('\n'))

* /

Удобен для поиска и замены, выделения и т. Д.

0 голосов
/ 01 ноября 2016
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)
...