第一条语句是函数定义:
var walk_the_DOM = function walk(node, func) {
...
};
这将函数分配给walk_the_DOM
。这个函数有两个参数:node
和func
。node
是您要处理的节点,func
也是您要应用的功能node
。
函数的第一行是func(node);
. 这实质上意味着您正在对node
. 例如,如果您walk_the_DOM
这样调用:
walk_the_DOM(root, function(node) {
console.log(node);
});
你会打电话
function(node) {
console.log(node);
}
在每个节点上,作为打印出树中每个节点的效果。
下一行node = node.firstChild;
基本上将 重新分配node
给它的第一个孩子。您需要这样做的原因是因为您需要查看当前节点的每个子节点。当然,您还需要查看这些孩子的孩子,但我们稍后会谈到那部分。
现在我们进入while
循环。这个while
循环的条件是 just while(node)
,这只是意味着只要未定义或未定义,循环就会node
运行null
。在我们之前的声明中,我们做到了node = node.firstChild
。如果当前节点没有子节点怎么办?然后node.firstChild
将为空,因此我们甚至不会进入循环。我们将退出它并退出函数(记住这部分;如果当前节点没有子节点,我们将退出函数。这被称为递归函数的停止条件)。
现在在while
循环内部,我们进行递归调用:walk(node, func);
. 让我们暂时忽略这里发生的事情,然后转到下一行:node = node.nextSibling;
. 在这里,我们将节点的下一个兄弟节点分配回变量node
。实际上,我们正在迭代这个节点的兄弟姐妹。现在如果节点没有其他兄弟节点(即父节点只有一个子节点)怎么办?然后node
将为空,我们将退出循环。
现在让我们回到递归调用walk(node, func)
。在递归调用中,我们调用函数本身,这意味着函数的行为与本次迭代完全相同。此时您可能会想“但这不意味着这将永远执行吗?”。但它不会!为什么?还记得我前面提到的停止条件吗?在某些时候,您将传入一个没有子节点的节点,这意味着递归调用将退出并返回到下一行(node = node.nextSibling
) 并且执行将正常进行。现在,如果您将 DOM 视为一棵树(它确实如此),这意味着您将尽可能地沿着一个分支向下移动,一旦到达终点,您就会回退一层,并检查以查看如果还有其他兄弟姐妹。如果有,你就尽可能地沿着那个分支走下去。如果没有,您会再退回一级并再次进行检查。这样,您就可以遍历整个 DOM 树。