我想使用递归原始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”为什么会这样?
node
有一些childNodes
,但没有一个孩子是element
时可能会出错。在这种情况下,nextchild = node.firstElementChild
将为null
,并且您在此处将其传递给recursiveCount(nextchild)
,而没有任何检查导致在此行上引发:if (node.childNodes) {
。
这是一种使用简单的相互递归来计数节点的方法。这将计算所有节点,包括文本节点-
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