首页 > 解决方案 > 如何将 jQuery ':not(:has(*)):not(a *):not(a)' 转换为 TreeWalker 的纯 js?

问题描述

我需要排除一些文本节点。为此我使用

$('body :not(:has(*)):not(script):not(textarea):not(textarea *):not(a *):not(a)')

我使用的功能是:

function findAllTextNodes(n) {
  var walker = n.ownerDocument.createTreeWalker(n, NodeFilter.SHOW_TEXT);
  var textNodes = [];
  while (walker.nextNode())
    if (walker.currentNode.parentNode.tagName != 'SCRIPT' && 
  walker.currentNode.parentNode.tagName != 'A' && walker.currentNode.parentNode.className != 'to-ignore')
      textNodes.push(walker.currentNode);
  return textNodes;
}

有没有更好、更易读的方法,我该如何做 ':not(:has(*))' 或 ':not(a *)'?

编辑:没有指向原始帖子的链接,但这里有指向jsfiddle的链接, 我也不希望里面的“the”<a><span>替换。

标签: javascripthtmldom

解决方案


Document.createTreeWalker构造函数可以包含一个带有过滤函数的 NodeFilter 对象,该函数测试由whatToShow参数选择每个节点。

对于通过测试的节点,过滤器函数应该返回值NodeFilter.FILTER_ACCEPT

测试节点时,您可以使用 DOM Element API 中的matches(selectorList)方法和您不想匹配的选择器列表。要么使用简单的列表并否定结果(如示例中所示),要么使用:not(selectorList)伪类

下面还过滤掉空文本节点和只有空格的节点,因为许多浏览器会插入空文本节点,其中 HTML 源代码在元素标记之间有任何空格(例如<p></p>,根据周围代码解析时可能有零到三个空文本节点) . 它还将实际文本推送到数组而不是文本节点对象中。

function findTextNodes() {
  var walker = document.createTreeWalker(
                 document.body,         // root
                 NodeFilter.SHOW_TEXT,  // nodes to include
                 {acceptNode: filter}   // NodeFilter object
               );
  var textNodes = [];
  while (walker.nextNode()) {
      textNodes.push(walker.currentNode.textContent);
  }
  return textNodes;
}

// NodeFilter function
function filter(node) {
  // Ignore any node that matches a selector in the list
  // and nodes that are empty or only whitespace
  if (!node.parentNode.matches('a, a *, script, textarea, .to-ignore') &&
      !/^\s*$/.test(node.textContent)
     ) {
    // If passes test, return accept value
    return NodeFilter.FILTER_ACCEPT;
  }
}

let textNodes = findTextNodes();
console.log(textNodes.join('\n'));
.to-ignore {
  background-color: yellow;
}

a * {
  color: green;
}
<p>It's the end of the world as we know it,<br>
   and I feel fine</p>
<a>the in a</a>
<br>
<a><span>the in span in a</span></a>
<span class="to-ignore">in to-ignore</span>

过滤器函数将忽略的节点是匹配以下任何选择器的节点:

  1. a - A 元素
  2. a * - A 元素的所有后代
  3. 脚本 - 脚本元素
  4. textarea - 文本区域元素
  5. .to-ignore - 具有“to-ignore”类的元素

推荐阅读