递归遍历DOM树

3
这是来自Crockford的JavaScript: The Good Parts书中的代码。
var results = [];

var walkDOM = function (node,func) {
        func(node);                     //What does this do?
        node = node.firstChild;
        while(node) {
            walkDOM(node,func);
            node = node.nextSibling;
        }

    };

我理解代码的意思,除了func(node)。我猜测这里是为了将node作为参数传递给func函数,但是浏览器如何理解呢?nodefunc可能是任何东西,所以当函数被调用时,它可能会像这样读取:
walkDOM(document.body,function(att) {
          node.getAttribute(att);
          results.push(node);
          });

当传入func时,walkDOM将处理函数(att) {...}(document.body)--这并没有任何意义。那么为什么Crockford选择包括func(node)呢?


2
预计使用 walkDOM 的开发人员将向其传递一个函数,该函数期望接收作为参数的 DOM。您的函数期望接收属性名称,因此不适合。 - user113716
@patrick_dw--我仍然不理解func(node)的符号表示法。开发者是否应该将其替换为function(node)?如果不是,那么浏览器如何理解func(node)呢? - dopatraman
浏览器不需要理解它。只有编写传递给 walkDOM 的函数的开发人员需要理解它。如果我想遍历DOM,针对每个文本节点,并将其文本替换为“patrick dw is sooooo good looking”,那么我会将该逻辑放在我传递的函数中。walkDOM将把每个节点(一个接一个地)传递给您的函数,而您的函数负责对该节点执行某些操作。 - user113716
请记住,func 只是对你传递给 walkDOM 的同一个函数的引用。 因此, func(node) 简单地调用您的函数,并将当前节点传递给它。 - user113716
1
你最终是否完全理解了walkTheDOM代码?如果没有,我很乐意帮忙。 - Imaginativeone
这个例子中的递归函数包括使用node.getAttribute,只是为了向新手学生演示不仅可以获取节点,还可以获取它们的属性。这样就预先回答了“如果可以像这样访问节点,是否可以访问属性?”的问题 - 这不是完整的代码,完整的代码应该包括像var attrVariable = node.getAttribute(“class”)这样的内容。我猜你可以迭代结果数组并检查那里的属性,但是如果你只想返回具有与特定类型匹配的属性的节点到该数组中,该怎么办? - miller the gorilla
4个回答

16

是的,这个树遍历函数很有道理,因为:

  • 函数参数应该知道如何处理(任意)节点
  • 第一个调用参数应该是一个支持的节点(例如,如果 func 不知道如何处理它,则不是 document.body

话虽如此,我认为这个算法仍然看起来是不正确的,因为它只遍历了第一个兄弟节点;其他兄弟节点将会被忽略。所以,我建议改用这种更传统的方法:

function walk(node, func) {
   var children = node.childNodes;
   for (var i = 0; i < children.length; i++)  // Children are siblings to each other
       walk(children[i], func);
   func(node);
}

请注意,版本为“深度优先”(即在处理子节点之前不调用func()),但我还是建议使用这种方式,因为func很可能会改变节点。这样父级的处理将能够考虑到其已处理的子级的最新状态,然后再发出可能不适当的更改。


6

我觉得func是用来对树中的每个节点进行操作的。

例如,如果我想要在整个树中警告每个节点的标签名称:

walkDOM(document.body, function(node) {
    alert(node.tagName);
});

在你的示例函数中:
walkDOM(document.body,function(att) {
      node.getAttribute(att);
      results.push(node);
      });

您将node参数命名为att,但这并不会神奇地使它成为属性的名称。当运行node.getAttribute(att)时,我会预期会出现“变量'node'未定义”,因为节点被设置为att……函数作用域中没有node


3

在 IT 技术中,func 是一个常用的函数。

func(node)

line通常被称为回调函数,它是由walkDOM函数可用的函数。使用更详细的函数名称可能更易理解:

var results = [];

var walkDOM = function (node, iCanBeCalledOnANode) {
    iCanBeCalledOnANode(node); // Will be called on every DOM element 
    node = node.firstChild;
    while(node) {
        walkDOM(node,func);
        node = node.nextSibling;
    }

};

希望这能为您解决问题。

0
Crockford的代码将回调传递给walkDOM,可以用于许多方式来处理传递给它的参数。
例如,可以编写一个方法,返回具有指定tagName的所有DOM元素的计数:
var getTagNameCount = function(tagName) {
    var count = 0;
    walkDOM(document.body, function(node) {
       if (node.tagName === tagName) {
          count++;
       }
    });
    return count;
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接