Javascript 将无序列表转换为JSON

Javascript 将无序列表转换为JSON,javascript,json,Javascript,Json,我试图从Javascript中的UL元素构建JSON对象 我有这样的列表结构: <ul id="trainingmenu"> <li class="tmnode"><span id="M5lx9n" class="tmname">menu 1</span> <ul> <li class="tmnode"><span id="M66p48" class="tmname"&g

我试图从Javascript中的UL元素构建JSON对象

我有这样的列表结构:

<ul id="trainingmenu">
    <li class="tmnode"><span id="M5lx9n" class="tmname">menu 1</span>
        <ul>
            <li class="tmnode"><span id="M66p48" class="tmname">menu 2</span>
                <ul>
                    <li class="tmnode"><span class="tmname" id="Mschr">menu 3</span>
                    </li>
                </ul>
            </li>
            <li class="tmnode"><span id="Mu03e0" class="tmname">menu 4</span></li>
        </ul>
    </li>
</ul>
这是部分工作,这是我想要的格式-从每个LI元素的childNode中选择id和name字段。但它是重复节点-菜单3出现两次

这里有一个


我可能把递归搞砸了,但我已经盯着它看了两天,还没有弄清楚是怎么回事。有人能帮忙吗?

它正在重复节点,因为
元素.getElementsByTagName
正在查找所有子代,而不仅仅是子代。您可能需要测试
nodeList[i]。parentNode==element


或者直接遍历元素的所有子节点(
element.childNodes
element.childrends
),并查找标记名为
的节点。在格式良好的HTML中,您应该只在
ul
元素的子列表中找到(空白)文本节点和
li
元素。

正如Alnitak提到的,您应该使用
.children
.childNodes
属性,而不是
.getElementsByTagName()
方法。以下代码段使用Array prototype的
.filter()
方法过滤
ul
子项,并使用
.forEach()
方法进行迭代

function treeHTML(element) {
    var o = {};
    o.id = element.firstElementChild.id; 
    o.name = element.firstElementChild.innerHTML;
    o.nodes = []; 
    [].slice.call(element.children).filter(function(e) {
        return e.tagName.toLowerCase() === 'ul';
    }).forEach(function(ul) {
        [].slice.call(ul.children).forEach(function(li) {
           o.nodes.push(treeHTML(li));
        });
    });
    return o;    
}
var treeObj = treeHTML(document.getElementById("trainingmenu")
                               .firstElementChild);
以下是一个替代解决方案,它也适用于旧版本的IE:

function children(node, selector) {
    var res = [], nodes = node.childNodes, l = nodes.length;
    for (var i = 0; i < l; i++) {
        if (nodes[i].nodeType === 1) {
            if (selector && nodes[i].tagName.toLowerCase() === selector) {
               res.push(nodes[i]);   
            } else if (!selector) {
               res.push(nodes[i]);
            }  
        }
    }
    return res;
}

function treeHTML(element) {
    var o = {};
    var span = children(element, 'span');
    o.id = span[0].id; 
    o.name = span[0].innerHTML;
    o.nodes = []; 
    var ul = children(element, 'ul'), n = ul.length;
    for (var i = 0; i < n; i++) {
        var li = children(ul[i]), l = li.length;
        for (var j = 0; j < l; j++) {
           o.nodes.push(treeHTML(li[j]));
        }
    }

    return o;    
}

var root = document.getElementById("trainingmenu");
var treeObj = treeHTML(children(root)[0]);
函数子节点(节点、选择器){
var res=[],nodes=node.childNodes,l=nodes.length;
对于(变量i=0;i

FWIW,我对代码的工作原理有点惊讶。如果HTML代码确实如所写,则外部
ul
上的
元素.firstChild
应返回
ul
li
之间的空白文本节点。在较新的浏览器中,您可以使用
.firstElementChild
忽略文本节点;我把小提琴上的断线去掉了。然而,我不知道。firstElementChild(!),所以我在那里学到了一些东西。谢谢。@WindsorAndy别忘了,
firstElementChild
是DOM规范中最近添加的一项内容,它可能不存在于较旧的浏览器上。。。。还有注释节点。@JanDvorak是的,那些也是!我决定继续使用getElementsByTagName(“LI”),但只将那些直接的后代传递到数组中进行进一步处理。因为在迭代过程中有一个UL元素,所以我觉得这太复杂了。你的洞察力让我明白了,非常感谢你非常优雅的解决方案,黑羊。这个问题对我来说变成了JS masterclass。@WindsorAndy谢谢,我很高兴它能帮上忙。你可以填充ES5数组函数,但你不能填充
firstElementChild
children
@Alnitak是的,你是对的,当IE8及以下版本不支持这些属性时填充ES5数组函数是毫无意义的。我会更新答案。@BlackSheep FWIW,我建议重新编写整个内容,只使用DOM0属性。它会稍微长一点,但完全可以随身携带。
function treeHTML(element) {
    var o = {};
    o.id = element.firstElementChild.id; 
    o.name = element.firstElementChild.innerHTML;
    o.nodes = []; 
    [].slice.call(element.children).filter(function(e) {
        return e.tagName.toLowerCase() === 'ul';
    }).forEach(function(ul) {
        [].slice.call(ul.children).forEach(function(li) {
           o.nodes.push(treeHTML(li));
        });
    });
    return o;    
}
var treeObj = treeHTML(document.getElementById("trainingmenu")
                               .firstElementChild);
function children(node, selector) {
    var res = [], nodes = node.childNodes, l = nodes.length;
    for (var i = 0; i < l; i++) {
        if (nodes[i].nodeType === 1) {
            if (selector && nodes[i].tagName.toLowerCase() === selector) {
               res.push(nodes[i]);   
            } else if (!selector) {
               res.push(nodes[i]);
            }  
        }
    }
    return res;
}

function treeHTML(element) {
    var o = {};
    var span = children(element, 'span');
    o.id = span[0].id; 
    o.name = span[0].innerHTML;
    o.nodes = []; 
    var ul = children(element, 'ul'), n = ul.length;
    for (var i = 0; i < n; i++) {
        var li = children(ul[i]), l = li.length;
        for (var j = 0; j < l; j++) {
           o.nodes.push(treeHTML(li[j]));
        }
    }

    return o;    
}

var root = document.getElementById("trainingmenu");
var treeObj = treeHTML(children(root)[0]);