有什么方法可以获取textNode
文档中所有对象的集合?
getElementsByTagName()
对于Elements来说效果很好,但textNode
s不是Elements。
更新:我意识到这可以通过遍历DOM来完成-如以下建议。我知道如何编写一个DOM-walker函数来查看文档中的每个节点。我希望有一些浏览器本机的方法可以做到这一点。毕竟,我可以<input>
通过一个内置调用获得所有s,但不是全部textNode
s有点奇怪。
Answers:
更新:
我已经概述了这6种方法在1000次运行中的每种的一些基本性能测试。getElementsByTagName
是最快的,但是它完成了一半的工作,因为它没有选择所有元素,而是仅选择一种特定类型的标签(我认为p
),并且盲目地假定其firstChild是文本元素。它可能没有什么瑕疵,但是它只是出于演示目的,并将其性能与TreeWalker
。在jsfiddle上运行测试以查看结果。
让我们暂时假设有一种方法可以让您Text
本地获取所有节点。您仍然必须遍历每个结果文本节点并调用node.nodeValue
以获取实际文本,就像处理任何DOM节点一样。因此,性能问题不在于遍历文本节点,而在于遍历非文本的所有节点并检查其类型。我会争论(基于结果),其TreeWalker
执行速度与一样快getElementsByTagName
,甚至还不及(即使getElementsByTagName播放有障碍)。
每次测试跑1000次。 方法总ms平均ms -------------------------------------------------- document.TreeWalker 301 0.301 迭代遍历769 0.769 递归遍历器7352 7.352 XPath查询1849 1.849 querySelector全部1725 1.725 getElementsByTagName 212 0.212
每种方法的来源:
树行者
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);
}
}
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
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;
}
/ *您可以返回某个父元素的所有后代文本节点的数组,也可以将其传递给某些函数,然后对文本进行某些操作(查找或替换或其他操作)。
此示例返回正文中非空白textnode的文本:
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;
}