使用递归JS函数计算DOM树中的节点和元素

问题描述 投票:0回答:2

我想使用递归原始JavaScript函数计算DOM树的所有节点和元素(在两个不同的变量中)

我尝试过类似的事情

    let nodeSum = 0;

function recursiveCount(node) {
  if (node.childNodes) {
    let childnodes = node.childNodes;
    nodeSum += childnodes.length;
    let nextchild = node.firstElementChild;
    let nextsibling = node.nextElementSibling;
    if (nextsibling) {
      return recursiveCount(nextsibling);
    }
    return recursiveCount(nextchild);
  }
  return;
}
recursiveCount(document);

这是错误TypeError:无法读取null的属性“ childNodes”为什么会这样?

javascript html dom
2个回答
0
投票

node有一些childNodes,但没有一个孩子是element时可能会出错。在这种情况下,nextchild = node.firstElementChild将为null,并且您在此处将其传递给recursiveCount(nextchild),而没有任何检查导致在此行上引发:if (node.childNodes) {


0
投票

这是一种使用简单的相互递归来计数节点的方法。这将计算所有节点,包括文本节点-

const count = ({ childNodes = [] }) =>
  1 + countChildren([...childNodes])
    
const countChildren = (nodes = []) =>
  nodes.reduce((r, n) => r + count(n), 0)

const e =
  document.querySelector('article')

console.log(count(e))
// 23
<article>
  <h1>Lorem Ipsum...</h1>
  <p>foo bar qux</p>
  <ul>
    <li>a</li>
    <li>b</li>
    <li>c</li>
  </ul>
  <p>foo bar qux</p>
</article>

如您所见,遍历节点的逻辑与计数逻辑纠缠在一起。通过将遍历分离为自己的函数,我们可以轻松执行各种计算,而无需重复遍历逻辑。

[C0以下]取决于count,将其唯一的重点放在特定(可能是复杂的)计数逻辑上-

traverse
const traverse = function* (node = {})
{ yield node                           // include this node
  for (const child of node.childNodes) // and for each of this node's children
    yield* traverse(child)             // traverse each child
}

const count = (node = {}) =>
  Array                               // create array
    .from(traverse(node))             // of all nodes
    .reduce                           // then reduce
      ( ([ nodes, elems ], { nodeType }) => // each node
          nodeType === 1                    // if node is Element type,
            ? [ nodes + 1, elems + 1 ]      // count as node and elem
            : [ nodes + 1, elems ]          // otherwise just count as node
      , [ 0, 0 ]                      // using these initial counts
      )

const e =
  document.querySelector('article')

const [ nodes, elems ] =
  count(e)

console.log(`nodes: ${nodes}, elems: ${elems}`)
// nodes: 23, elems: 8

如果目标仅是计算<article> <h1>Lorem Ipsum...</h1> <p>foo bar qux</p> <ul> <li>a</li> <li>b</li> <li>c</li> </ul> <p>foo bar qux</p> </article>个节点,我们可以简化Element-

reduce
const traverse = function* (node = {})
{ yield node                           // include this node
  for (const child of node.childNodes) // and for each of this node's children
    yield* traverse(child)             // traverse each child
}

const count = (node = {}) =>
  Array                      // create array
    .from(traverse(node))    // of all nodes
    .reduce                  // then reduce
      ( (r, { nodeType }) => // each node
          nodeType === 1     // if node is Element type,
            ? r + 1          // increase count by one
            : r              // otherwise keep count the same
      , 0                    // using this initial count
      )

const e =
  document.querySelector('article')

const elems =
  count(e)

console.log(`elems: ${elems}`)
// elems: 8
© www.soinside.com 2019 - 2024. All rights reserved.