Обновление :
Я обрисовал в общих чертах некоторые базовые тесты производительности для каждого из этих 6 методов за 1000 запусков. getElementsByTagName
является самым быстрым, но он выполняет половинчатую работу, поскольку он не выбирает все элементы, а только один конкретный тип тега (я думаю p
) и вслепую предполагает, что его firstChild является текстовым элементом. Это может быть немного ошибочно, но оно предназначено для демонстрации и сравнения его производительности TreeWalker
. Проведите тесты на jsfiddle, чтобы увидеть результаты.
- Использование TreeWalker
- Пользовательский итеративный обход
- Пользовательский рекурсивный обход
- Xpath запрос
- querySelectorAll
- getElementsByTagName
Предположим на мгновение, что существует метод, позволяющий получить все Text
узлы изначально. Вам все равно придется обходить каждый результирующий текстовый узел и вызывать node.nodeValue
для получения фактического текста, как если бы вы поступали с любым узлом DOM. Таким образом, проблема производительности заключается не в итерации текстовых узлов, а в повторении всех узлов, не являющихся текстовыми, и проверке их типа. Я бы поспорил (основываясь на результатах), что он TreeWalker
работает так же быстро getElementsByTagName
, если не быстрее (даже если getElementsByTagName играет с ограниченными возможностями).
Выполните каждый тест 1000 раз.
Метод Всего мс Среднее мс
--------------------------------------------------
document.TreeWalker 301 0,301
Итерационный Traverser 769 0,769
Рекурсивный Traverser 7352 7.352
Запрос XPath 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) {
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 (гандикап)
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);
}
}
Кроме того, это обсуждение может оказаться полезным - http://bytes.com/topic/javascript/answers/153239-how-do-i-get-elements-text-node
node.nodeType = 3
наnode.nodeType == 3
=
ошибку. Я исправил это, и версия xpath просто возвращалаText
объекты, а не фактическую строку, содержащуюся в ней, как это делали другие методы. Метод, который получает текст только первого потомка, намеренно неправильный, и я упоминал об этом в начале. Я повторно проведу тесты и опубликую обновленные результаты здесь. Все тесты (кроме getElementsByTagName и xpath) возвращают одинаковое количество текстовых узлов. XPath сообщает примерно на 20 узлов больше, чем другие, которые я пока игнорирую.Вот современная
Iterator
версия самого быстрого метода 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; }
Применение:
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; }
источник
Я знаю, что вы специально просили коллекцию, но если вы имели в виду это неформально и не заботились о том, были ли все они объединены в одну большую строку, вы можете использовать:
var allTextAsString = document.documentElement.textContent || document.documentElement.innerText;
... с первым элементом стандартного подхода DOM3. Однако обратите внимание, что
innerText
похоже, исключает содержимое тега сценария или стиля в реализациях, которые его поддерживают (по крайней мере, IE и Chrome), ноtextContent
включает их (в Firefox и Chrome).источник
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'))
* /
Удобен для поиска и замены, выделения и т. Д.
источник
Вот альтернатива, которая немного более идиоматична и (надеюсь) легче для понимания.
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 } } }
источник
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)
источник
после того,
createTreeWalker
как устарел, вы можете использовать/** * 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; }
источник