递归函数不捕获父DOM节点属性

时间:2013-06-28 14:51:43

标签: javascript dom

我编写了一个递归函数,遍历以下形式的嵌套DOM节点:

<a href="#" title="test">
    <div id="nested-image">
          <img src="image.jpg" />
    </div>
</a>

递归函数如下:

function getNestedNodes(nodeList) {
var ary = [];
for(var i = 0; i < nodeList.length; ++i) {
    var myJSONChildren = {};
    if(nodeList[i].childElementCount) {
        var htmlCollection = nodeList[i].children;
        for(var j = 0; j < htmlCollection.length; ++j) {
            for(var k =0, attrs = htmlCollection[j].attributes, l = attrs.length; k < l; ++k) {
                myJSONChildren['tag'] = htmlCollection[j].nodeName;
                myJSONChildren[attrs.item(k).nodeName] = attrs.item(k).nodeValue;
            };
        }
        myJSONChildren['children'] = getNestedNodes(htmlCollection);
        ary.push(myJSONChildren);
    };
}
return ary;
}

所以,如果我以这种方式调用该函数:

var links = document.querySelectorAll('a');
console.log(JSON.stringify(getNestedNodes(links)));

它应该返回以下形式的JSON数组:

[{
  tag:'a',
  href:"#",
  title:"test",
  children:[{
             tag:"div",
             id:"nested-image",
             children:[{
                        tag:"img",
                        src:"image.jpg"
                       }]
             }]
  }]
}]

但是,它只返回以下形式之一:

 [{
   "tag":"DIV",
   "id":"nested-image",
   "children":[{
                "tag":"IMG",
                 "src":"https://www.gravatar.com/avatar/d1a336ae4b6876a4c5c044ec17876ce0",
                "children":[]
              }]
 }] 

并且我无法以正确的方式获取我想要的表单而不会得到空的结果或重复的结果。

另外,我想优化我的递归函数,我确信我可以重构为更具可读性的东西。

这是一个小提琴,你可以看到:

http://jsfiddle.net/DfHqv/

任何帮助将不胜感激!

3 个答案:

答案 0 :(得分:3)

问题是你已经使你的功能预期接收一个集合,但你正在循环两个节点集合。

所以看起来你并没有从外循环中推送节点,而只是推送子节点。这可以解释为什么你没有达到顶级水平。

只需将它保存到单个循环的节点(然后是属性的一个循环),然后在新循环中循环子节点,只需在递归调用中传递它

function getNestedNodes(nodeList) {
    var ary = [];

    // Loop the collection of nodes
    for(var i = 0; i < nodeList.length; ++i) {
        var node = nodeList[i];

        // Create the new object with the "tag" populated
        var jsonNode = {"tag":node.nodeName};

        // Add the attributes to the object
        for(var k =0, attrs = node.attributes, l = attrs.length; k < l; ++k) {
            jsonNode[attrs.item(k).nodeName] = attrs.item(k).nodeValue;
        }

        // Make a recursive call if any children are present, and add the result
        if (node.children && node.children.length)
            jsonNode['children'] = getNestedNodes(node.children);

        ary.push(jsonNode);
    }
    return ary;
}

DEMO: http://jsfiddle.net/argA3/1/

[
    {
        "tag": "A",
        "title": "test",
        "href": "#",
        "children": [
            {
                "tag": "DIV",
                "id": "nested-image",
                "children": [
                    {
                        "tag": "IMG",
                        "src": "image.jpg"
                    }
                ]
            }
        ]
    }
]

答案 1 :(得分:1)

这似乎有效:

function getNestedNodes(nodeList) {
    var ary = [];
    for (var i = 0; i < nodeList.length; i += 1) {
        var attributes = {};
        for (var key in nodeList[i].attributes) {
            attributes[nodeList[i].attributes.item(key).nodeName] = nodeList[i].attributes.item(key).nodeValue;
        }
        ary.push({
            tag: nodeList[i].nodeName,
            attributes: attributes,
            children: getNestedNodes(nodeList[i].children)
        });
    }
    return ary;
}

var links = document.querySelectorAll('a');
console.log(JSON.stringify(getNestedNodes(links)));

输出:

[{"tag":"A","attributes":{"href":"#","title":"test"},"children":[{"tag":"DIV","attributes":{"id":"nested-image"},"children":[{"tag":"IMG","attributes":{"src":"https://www.gravatar.com/avatar/d1a336ae4b6876a4c5c044ec17876ce0?s=32&d=identicon&r=PG"},"children":[]}]}]}] 

答案 2 :(得分:1)

由于我们都在进行各种(重构)出价:

// getNestedNodes2(nodeList)
// @nodeList: a collection of nodes to serialize
function getNestedNodes2(nodeList){
    // iterate over the node collection
    for (var i = 0, result = []; i < nodeList.length; i++){
        // begin building a definition of the current node
        var thisNode = {
            tag: nodeList[i].tagName
        };
        // iterate over any attributes on the current node and add them
        // to the current definition.
        for (var j = 0, attributes = nodeList[i].attributes; j < attributes.length; j++){
            thisNode[attributes.item(j).nodeName] = attributes.item(j).nodeValue;
        }
        // check for child elements and, if present, also add them to
        // the definition
        if (nodeList[i].childElementCount > 0){
            thisNode.children = getNestedNodes2(nodeList[i].children);
        }
        // add the definition to the results set
        result.push(thisNode);
    }
    // return the results
    return result;
}

end result

[
  {
    "tag": "A",
    "title": "test",
    "href": "#",
    "children": [
      {
        "tag": "DIV",
        "id": "nested-image",
        "children": [
          {
            "tag": "IMG",
            "src": "https://www.gravatar.com/avatar/d1a336ae4b6876a4c5c044ec17876ce0?s=32&d=identicon&r=PG"
          }
        ]
      }
    ]
  }
]

请记住,您正在接受集合,这意味着当您直接迭代它们并且只构建子集的定义时,将忽略第一次调用中的节点。相反,首先处理收到的集合中的节点,然后让递归照顾孩子们。