我编写了一个递归函数,它遍历以下形式的嵌套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/
任何帮助都将不胜感激!
问题是,您已经使函数预期接收到一个集合,但却在其中循环两个节点集合。
因此,您似乎并没有从外循环推入节点,而是只推入子节点。这可以解释为什么你没有达到最高水平。
只需将其保留在节点的单个循环中(当然还有属性的循环),然后不在新循环中循环子级,而是在递归调用中传递它。
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;
}
演示:http://jsfiddle.net/argA3/1/
[
{
"tag": "A",
"title": "test",
"href": "#",
"children": [
{
"tag": "DIV",
"id": "nested-image",
"children": [
{
"tag": "IMG",
"src": "image.jpg"
}
]
}
]
}
]
这似乎有效:
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":[]}]}]}]
因为我们都有各种(重构的)出价:
// 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;
}
最终结果:
[
{
"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"
}
]
}
]
}
]
请记住,接受中的集合,这意味着当您直接对第一个调用中的节点进行迭代并仅构建子节点的定义时,它们将被忽略。相反,首先处理接收到的集合中的节点,然后让递归处理子节点。