在HTML页面中查找所有文本节点

71

我需要在这个问题中找到特定节点下的所有文本节点。我可以像这样做:

function textNodesUnder(root){
  var textNodes = [];
  addTextNodes(root);
  [].forEach.call(root.querySelectorAll('*'),addTextNodes);
  return textNodes;

  function addTextNodes(el){
    textNodes = textNodes.concat(
      [].filter.call(el.childNodes,function(k){
        return k.nodeType==Node.TEXT_NODE;
      })
    );
  }
}

然而,在XPath的帮助下,只需查询.//text()就可以完成这项工作,因此这种方法似乎不太优雅。

在支持IE9+、Safari5+、Chrome19+、Firefox12+和Opera11+的浏览器中,获取特定元素下的所有文本节点的最简单方法是什么?

“最简单”的定义不严格,意思是“高效且简短,但不要过度压缩代码”。


1
https://dev59.com/QXE85IYBdhLWcg3w2Hbs - Ja͢ck
哎呀,糟糕了。 谢谢,杰克,我搜索了但没找到那个问题。 - Phrogz
是的,我也不知道为什么它没有出现在侧边栏中,但是我在进行谷歌搜索时找到了它 :) - Ja͢ck
2个回答

196

根据@kennebec的答案,以下是同样逻辑略微更紧凑的实现:

function textNodesUnder(node){
  var all = [];
  for (node=node.firstChild;node;node=node.nextSibling){
    if (node.nodeType==3) all.push(node);
    else all = all.concat(textNodesUnder(node));
  }
  return all;
}

不过,更快、更紧凑、更优雅的方法是使用createTreeWalker函数,这样浏览器就能为你过滤掉除文本节点以外的所有内容:

function textNodesUnder(el){
  var n, a=[], walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false);
  while(n=walk.nextNode()) a.push(n);
  return a;
}

14
在我的电脑上,使用Chrome v50查找此页面上的所有文本节点,使用第一种技术需要1900μs,但是使用TreeWalker技术只需要220μs。因此,快了8或9倍。 - Phrogz
3
为了排除<script>元素的内容,我不得不对此进行微调:https://gist.github.com/Daniel-Hug/1415b4d027e3e9854456f4e812ea2ce1 - Web_Designer
1
如果您正在使用TreeWalker方法,并且想要像Web_Designer提到的那样排除脚本或样式标签,您可以将过滤器作为第三个参数传递给createTreeWalker函数。 - Vinay Pai
1
@VinayPai - 注意:filter仅在已通过whatToShow检查的节点上运行,因此在这种情况下,您无法使用方便的NodeFilter.SHOW_TEXT,而是必须添加额外的逻辑来手动过滤文本节点,例如通过nodeType - Sphinxxx
2
@Web_Designer - 在仍使用 document.createTreeWalker() 的情况下的替代方案:https://gist.github.com/Sphinxxxx/ed372d176c5c2c1fd9ea1d8d6801989b - Sphinxxx
显示剩余2条评论

6
function deepText(node){
    var A= [];
    if(node){
        node= node.firstChild;
        while(node!= null){
            if(node.nodeType== 3) A[A.length]=node;
            else A= A.concat(deepText(node));
            node= node.nextSibling;
        }
    }
    return A;
}

1
不加 != nullwhile (node) 怎么样? - Phrogz
3
甚至可以使用for (node=node.firstChild;node;node=node.nextSibling){ … } - Phrogz
1
我曾担心递归解决方案可能会遇到堆栈限制问题,但是现在我明白这是不太可能的。参考链接:https://dev59.com/0Wsz5IYBdhLWcg3wiYY0。 - Phrogz
1
一旦你知道第一个(父)节点是一个子节点,那么node.nextSibling的唯一可能值就是另一个子节点或null。 - kennebec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接