JavaScript获取含文本HTML元素时过滤指定元素及其子节点
问题原因
代码失效核心是3个问题:
- 传入
NodeFilter.SHOW_TEXT配置后,TreeWalker遍历会直接跳过所有元素节点,你没有机会对需要排除的父元素触发FILTER_REJECT逻辑——这个返回值的作用是同时拒绝当前节点+它的所有后代节点,只判断文本节点根本没法拦截整棵子树。 - 原有过滤逻辑只判断文本节点的直接父元素,嵌套在排除标签内部的子标签(比如pre里的p)下的文本,直接父元素不在排除列表里,就会漏判。
- 传入的第四个参数
true是已废弃的实体引用展开配置,在HTML文档中无任何效果,实现不了跳过子节点的作用;同时你漏写了过滤class为this_is_code的div的逻辑。
修正方案
调整TreeWalker的遍历配置,同时处理元素节点和文本节点:遇到需要排除的元素节点直接返回FILTER_REJECT跳过自身和所有后代,遇到普通元素跳过节点本身但继续遍历子节点,遇到非空文本节点才收集。
修正后的完整代码如下:
<!DOCTYPE html> <html> <head> <script> function nativeTreeWalker() { // 配置要排除的标签名 const EXCLUDE_TAGS = ['STYLE', 'SCRIPT', 'PRE']; // 配置要排除的类名 const EXCLUDE_CLASS = 'this_is_code'; var walker = document.createTreeWalker( document.body, NodeFilter.SHOW_ALL, // 遍历所有节点,才能拦截要排除的父元素 { acceptNode: function(node) { // 处理元素节点:判断是否需要排除整棵子树 if (node.nodeType === Node.ELEMENT_NODE) { if ( EXCLUDE_TAGS.includes(node.nodeName) || node.classList.contains(EXCLUDE_CLASS) ) { // 拒绝当前元素+所有后代节点,直接跳过整棵子树 return NodeFilter.FILTER_REJECT; } // 普通元素不需要收集,跳过节点本身但继续遍历子节点 return NodeFilter.FILTER_SKIP; } // 处理文本节点:过滤空白文本 if (node.nodeType === Node.TEXT_NODE) { if (!/^\s*$/.test(node.data)) { return NodeFilter.FILTER_ACCEPT; } } return NodeFilter.FILTER_REJECT; } } ); var node; var textNodes = []; while(node = walker.nextNode()){ textNodes.push(node.nodeValue); } return textNodes } window.onload = function(){ console.log(nativeTreeWalker()) } </script> </head> <body> get the text <p> </p> <div>This is text, get</div> <p>This is text, get too</p> <pre> This is code,Don't get <p>this is code too, don't get</p> </pre> <div class="this_is_code"> This is className is code, Don't get <span>this is code too, don't get</span> </div> </body></html>
运行后输出结果和预期完全一致,共3条有效文本节点,且TreeWalker的遍历性能和原方案一致,远高于querySelectorAll的实现。
提示:如果后续要加其他排除规则,只需要在元素节点判断的分支里加对应条件即可,只要命中排除规则返回
FILTER_REJECT,对应元素下的所有内容都会被自动过滤。
内容的提问来源于stack exchange,提问作者dong
相关产品推荐
相关产品推荐

