javascript 我如何从 JSON 数据递归创建 UL/LI 的 - 多层深度

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9362446/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 06:24:39  来源:igfitidea点击:

How can I recursively create a UL/LI's from JSON data - multiple layers deep

javascriptjsonrecursionmenu

提问by quinton

I am trying to use use the following JSON data to create the following similar structure in a recursive inner function with not much luck, really need some help and so if anyone can assist please do. Thank you in advance.

我正在尝试使用以下 JSON 数据在递归内部函数中创建以下类似的结构,但运气不佳,确实需要一些帮助,因此如果有人可以提供帮助,请执行。先感谢您。

<ul>
    <li></li>
    <li>
        <a href=""></a>
        <div>
            <ul>
                <li>
                    <a href=""></a>
                    <div>
                         ....etc
                    </div>
                </li>
            </ul>
        </div>
    </li>
</ul>

the JSON data I am using is as follows:

我使用的 JSON 数据如下:

    var JSON = {
    menu: [
        {id: '0',sub: [
            {name: 'lorem ipsum 0-0',link: '0-0', sub: null},
            {name: 'lorem ipsum 0-1',link: '0-1', sub: null},
            {name: 'lorem ipsum 0-2',link: '0-2', sub: null}
            ]
        },
        {id: '1',sub: null},
        {id: '2',sub: [
            {name: 'lorem ipsum 2-0',link: '2-0', sub: null},
            {name: 'lorem ipsum 2-1',link: '2-1', sub: null},
            {name: 'lorem ipsum 2-2',link: '2-2', sub: [
                {name: 'lorem ipsum 2-2-0',link: '2-2-0', sub: null},
                {name: 'lorem ipsum 2-2-1',link: '2-2-1', sub: null},
                {name: 'lorem ipsum 2-2-2',link: '2-2-2', sub: null},
                {name: 'lorem ipsum 2-2-3',link: '2-2-3', sub: null},
                {name: 'lorem ipsum 2-2-4',link: '2-2-4', sub: null},
                {name: 'lorem ipsum 2-2-5',link: '2-2-5', sub: null},
                {name: 'lorem ipsum 2-2-6',link: '2-2-6', sub: null}
            ]},
            {name: 'lorem ipsum 2-3',link: '2-3', sub: null},
            {name: 'lorem ipsum 2-4',link: '2-4', sub: null},
            {name: 'lorem ipsum 2-5',link: '2-5', sub: null}
            ]
        },
        {id: '3',sub: null}
    ]
}

and the code I have created (incomplete, this is the brain teaser I need help on) is:

我创建的代码(不完整,这是我需要帮助的脑筋急转弯)是:

$(function(){

    $.fn.dropdown = function(settings){
        var that = this;
        var settings = $.extend({}, $.fn.dropdown.defaults, settings);
        var methods = {
            isArray: function(o){
                return Object.prototype.toString.call(o) === '[object Array]';
            },
            createDropdownCode: function(arr){
                var menu = arr.menu;
                var html = null;
                var menusort = function(menu){
                    html = that;

                    that.find("li").each(function(idx){

                        var menuList = menu[idx].sub;
                        var baseContainer = $(this);
                        var count = -1;

                        var subsort = (function(){

                            count += 1;

                            return function(submenu, pb){

                                var subblock;
                                subblock = $("<div />").append('<ul />');

                                if(methods.isArray(submenu)){

                                    for(var i=0;i<submenu.length;i++){

                                        var l = $("<li />").append("<a href='"+ submenu[i].link +"'>"+ submenu[i].name +"</a>");

                                        subblock.find('ul').append(l);

                                        if(pb !== undefined && i == submenu.length-1){
                                            pb.append(subblock)
                                        }

                                        if(methods.isArray(submenu[i].sub)){
                                            subsort(submenu[i].sub, subblock.find('ul li').eq(i));
                                        }

                                    }
                                }
                            }
                        })()
                        subsort(menuList)
                    })
                }
                menusort(menu);
                return null; //html !== null ? html.html() : null;
            },
            init: function(){

                // filter through json
                // create the div=>ul=>li
                if(settings.jsonData === undefined || settings.jsonData === null){
                    console.warn('No JSON Data passed')
                    return;
                }else{
                    if(!methods.isArray(settings.jsonData.menu)){
                        console.warn('No JSON Data passed')
                        return; // error, no data!
                    }
                }


                //var html = methods.createBlock(settings.jsonData.menu[0].sub);
                var html = methods.createDropdownCode(settings.jsonData);
                //console.log(html) 
            }
        }

        methods.init();

        return that;

    }

    $.fn.dropdown.defaults = {
        jsonData: null
    }

})

$('#menu').dropdown({
    jsonData: JSON
});

integrated code used, thanks to the individual that gave a close enough answer - Although will study the others.

使用的集成代码,感谢给出足够接近答案的个人 - 虽然会研究其他人。

$.fn.dropdown = function(settings){

    var that = this;
    var settings = $.extend({}, $.fn.dropdown.defaults, settings);

    var methods = {
        createDropDownCode: function(arr){

            // loop through li's of primary menu
            that.find("li").each(function(idx){

                $(this).append( menusort(arr.menu[idx].sub) );

                function menusort(data){
                    if(data !== null)   
                        var html = "<div><ul>";

                    for(item in data){
                        html += "<li>";
                        if(typeof(data[item].sub) === 'object'){
                            html += "<a href='" + data[item].link + "'>" + data[item].name + "</a>";
                            if($.isArray(data[item].sub))
                                html += menusort(data[item].sub);
                        }
                        html += "</li>"
                    }
                    if(data !== null)
                        html += "</ul></div>";
                    return html;
                }
            })
        },
        init: function(){
            var html = methods.createDropDownCode(settings.jsonData);

        }
    }

    methods.init();

}

回答by Rémi Breton

You can try this recursive function I've just coded:

你可以试试我刚刚编写的这个递归函数:

function buildList(data, isSub){
    var html = (isSub)?'<div>':''; // Wrap with div if true
    html += '<ul>';
    for(item in data){
        html += '<li>';
        if(typeof(data[item].sub) === 'object'){ // An array will return 'object'
            if(isSub){
                html += '<a href="' + data[item].link + '">' + data[item].name + '</a>';
            } else {
                html += data[item].id; // Submenu found, but top level list item.
            }
            html += buildList(data[item].sub, true); // Submenu found. Calling recursively same method (and wrapping it in a div)
        } else {
            html += data[item].id // No submenu
        }
        html += '</li>';
    }
    html += '</ul>';
    html += (isSub)?'</div>':'';
    return html;
}

It returns the html for the menu, so use it like that: var html = buildList(JSON.menu, false);

它返回菜单的 html,所以像这样使用它: var html = buildList(JSON.menu, false);

I believe it is faster because it's in pure JavaScript, and it doesn't create text nodes or DOM elements for every iteration. Just call .innerHTMLor $('...').html()at the end when you're done instead of adding HTML immediately for every menu.

我相信它更快,因为它使用纯 JavaScript,并且不会为每次迭代创建文本节点或 DOM 元素。只需在完成后调用.innerHTML$('...').html(),而不是立即为每个菜单添加 HTML。

JSFiddled: http://jsfiddle.net/remibreton/csQL8/

JSFiddled:http: //jsfiddle.net/remibreton/csQL8/

回答by georg

Make two functions makeULand makeLI. makeULcalls makeLIon each element, and makeLIcalls makeULif there's subelements:

制作两个函数makeULmakeLImakeUL调用makeLI每个元素,如果有元素则makeLI调用:makeULsub

function makeUL(lst) {
    ...
    $(lst).each(function() { html.push(makeLI(this)) });
    ...
    return html.join("\n");
}

function makeLI(elem) {
    ...
    if (elem.sub)
        html.push('<div>' + makeUL(elem.sub) + '</div>');
    ...
    return html.join("\n");
}

http://jsfiddle.net/BvDW3/

http://jsfiddle.net/BvDW3/

Needs to be adapted to your needs, but you got the idea.

需要根据您的需求进行调整,但您明白了。

回答by Serhii Bilyk

Pure ES6

纯 ES6

var foo=(arg)=>
`<ul>
${arg.map(elem=>
    elem.sub?
        `<li>${foo(elem.sub)}</li>`
        :`<li>${elem.name}</li>`
    )}
</ul>`

JSON example

JSON 示例

   var bar = [
  {
    name: 'Home'
  }, {
    name: 'About'
  }, {
    name: 'Portfolio'
  }, {
    name: 'Blog'
  }, {
    name: 'Contacts'
  }, {
    name: 'Features',
    sub: [
      {
        name: 'Multipage'
      }, {
        name: 'Options',
        sub: [
          {
            name: 'General'
          }, {
            name: 'Sidebars'
          }, {
            name: 'Fonts'
          }, {
            name: 'Socials'
          }
        ]
      }, {
        name: 'Page'
      }, {
        name: 'FAQ'
      }
    ]
  }
]
var result=foo(bar)

Your 'result' will be valid HTML

您的“结果”将是有效的 HTML

回答by Mr. Polywhirl

This solution uses a single recursive function. I simplified logic by using Array's map()prototypefunction.

此解决方案使用单个递归函数。我通过使用Arraymap()prototype功能简化了逻辑。

$(function () {
    $("body").html(makeUnorderedList(getData().menu));
});

function makeUnorderedList(data, li) {
    return $('<ul>').append(data.map(function (el) {
        var li = li || $('<li>');
        if (el.id || el.link) li.append($('<a>', {
            text : el.id || el.link,
            href : '#' + (el.id || el.link),
            name : el.name
        }));
        if (el.sub) li.append(makeUnorderedList(el.sub, li));
        return li;
    }));
}

function getData() {
    return {
        menu: [{
            id: '0',
            sub: [{
                name: 'lorem ipsum 0-0',
                link: '0-0',
                sub: null
            }, {
                name: 'lorem ipsum 0-1',
                link: '0-1',
                sub: null
            }, {
                name: 'lorem ipsum 0-2',
                link: '0-2',
                sub: null
            }]
        }, {
            id: '1',
            sub: null
        }, {
            id: '2',
            sub: [{
                name: 'lorem ipsum 2-0',
                link: '2-0',
                sub: null
            }, {
                name: 'lorem ipsum 2-1',
                link: '2-1',
                sub: null
            }, {
                name: 'lorem ipsum 2-2',
                link: '2-2',
                sub: [{
                    name: 'lorem ipsum 2-2-0',
                    link: '2-2-0',
                    sub: null
                }, {
                    name: 'lorem ipsum 2-2-1',
                    link: '2-2-1',
                    sub: null
                }, {
                    name: 'lorem ipsum 2-2-2',
                    link: '2-2-2',
                    sub: null
                }, {
                    name: 'lorem ipsum 2-2-3',
                    link: '2-2-3',
                    sub: null
                }, {
                    name: 'lorem ipsum 2-2-4',
                    link: '2-2-4',
                    sub: null
                }, {
                    name: 'lorem ipsum 2-2-5',
                    link: '2-2-5',
                    sub: null
                }, {
                    name: 'lorem ipsum 2-2-6',
                    link: '2-2-6',
                    sub: null
                }]
            }, {
                name: 'lorem ipsum 2-3',
                link: '2-3',
                sub: null
            }, {
                name: 'lorem ipsum 2-4',
                link: '2-4',
                sub: null
            }, {
                name: 'lorem ipsum 2-5',
                link: '2-5',
                sub: null
            }]
        }, {
            id: '3',
            sub: null
        }]
    };
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>



Extension

延期

Here is a more dynamic approach. You get to choose how your list items are rendered and what the child property is. The mapFuncparamater is a callback that gives you access to the current child node and its parent.

这是一种更动态的方法。您可以选择如何呈现列表项以及子属性是什么。该mapFuncparamater是一个回调,让你访问当前子节点和其父。

The scope of the mapFuncis the item. So you could use itemas well as thisto refer to said item.

的范围mapFunc是项目。所以你可以使用itemasthis来指代item.

$(function () {
    $("body").html(makeUnorderedList(getData().menu, function(item, index, parent) {
      // `item` and `this` are the same.
      return $('<a>', {
            text : (item.id || item.link),
            href : '#' + (item.id || item.link),
            name : item.name,
            'data-index' : index
        });
    }, 'sub'));
});

function makeUnorderedList(data, mapFunc, childProp, li, parent) {
    return $('<ul>').append(data.map(function (el, index) {
        var li = li || $('<li>');
        li.append(mapFunc.call(el, el, index, parent));
        if (el[childProp]) {
            li.append(makeUnorderedList(el[childProp], mapFunc, childProp, li, data));
        }
        return li;
    }));
}

回答by Nadeemmnn Mohd

Fiddle

小提琴

Code:

代码:

JS

JS

var jsonstring = [{
  "id": '1',
  "children": [{
    "id": '2'
  }, {
    "id": '3',
    "children": [{
      "id": '4'
    }]
  }]
}, {
  "id": '5'
}];

 var htmlStr= recurse( jsonstring );
$('#test').append(htmlStr);


function recurse( data ) {
  var htmlRetStr = "<ul>"; 
  for (var key in data) { 
        if (typeof(data[key])== 'object' && data[key] != null) {
        var x=key*1;        
        if(isNaN(x)){
            htmlRetStr += "<li>" + key + ":<ul>";
            }
            htmlRetStr += recurse( data[key] );
            htmlRetStr += '</ul></li>';
        } else {
            htmlRetStr += ("<li>" + key + ': &quot;' + data[key] + '&quot;</li  >' );
        }
  };
  htmlRetStr += '</ul >';    
  return( htmlRetStr );
}

HtML

网页语言

<div id="test"></div>

CSS

CSS

li ul ul li {
    padding-left: 10px;
}

li ul ul ul {
    padding: 0px;
}

回答by HumbleGeek

This is like a complete solution for generating UL/LI recursively from JSON config, which has customizable classes for each node and support of expand and collapse events for each node. This provides just a basic working model, from which you ll be able to expand and customize to your needs.

这就像一个从 JSON 配置递归生成 UL/LI 的完整解决方案,它为每个节点提供可自定义的类,并支持每个节点的展开和折叠事件。这仅提供了一个基本的工作模型,您可以从中扩展和定制您的需求。

I found this answer from https://techmeals.com/fe/questions/javascript/6/How-can-I-create-a-dynamic-tree-of-UL-and-LI-from-JSON-config

我从https://techmeals.com/fe/questions/javascript/6/How-can-I-create-a-dynamic-tree-of-UL-and-LI-from-JSON-config找到了这个答案

Example JSON config file:

JSON 配置文件示例:

var config = {
    "Menu-1-Level-1": {
        "label": "Menu-1-Level-1",
        "type": "treeView",
        "class": "Menu-1-Level-1",
        "children": [
            {
                label: "Menu-1-Level-2",
                type: "treeView",
                "class": "Menu-1-Level-2",
                children: [
                    {
                        label: "Menu-1-Level-3",
                        class: "Menu-1-Level-3"
                    }
                ]
            },
            {
                label : "Menu-2-Level-2",
                class: "Menu-2-Level-2"
            }
        ]
    },
    "Menu-2-Level-1": {
        "label": "Menu-2-Level-1",
        "type": "treeView",
        "class": "Menu-2-Level-1",
        "children": [
            {
                label: "Menu-1-Level-2",
                class: "Menu-1-Level-2",
                type: "treeView",
                children: [
                    {
                        label: "Menu-1-Level-3",
                        class: "Menu-1-Level-3"
                    }
                ]
            },
            {
                label : "Menu-2-Level-2",
                class : "Menu-2-Level-2"
            }
        ]
    }
};

HTML Code:

HTML代码:

<!DOCTYPE html>
<html>

<head>
    <title>Tree Menu</title>
    <script src="http://code.jquery.com/jquery-1.11.2.min.js" type="text/javascript"></script>
    <script src="tree.js" type="text/javascript"></script>
    <link href="tree.css" rel="stylesheet">
</head>

<body>
    <div class="treeContainer">
        <div class="tree"></div>
    </div>
    <script src="testPage.js" type="text/javascript"></script>
</body>

</html>

Tree.js

树.js

var tree;

tree = function (treeNodeParent, dataObj) {
    this.dataObj = dataObj;
    this.treeNodeParent = treeNodeParent;
    this.treeNode = $(document.createElement("ul")).addClass("treeNode");
};

tree.prototype.expandCollapse = function (e) {
    var target = $(e.currentTarget), parentLabel = target.parent();

    if (parentLabel.hasClass("collapsed")) {
        parentLabel.removeClass("collapsed").addClass("expanded");
    } else {
        parentLabel.addClass("collapsed").removeClass("expanded");
    }
};

tree.prototype.attachEvents = function () {
    var me = this;
    me.treeNodeParent.delegate(".collapsed label, .expanded label", "click", me.expandCollapse);
};

tree.prototype.attachMarkUp = function () {
    var me = this;
    me.treeNodeParent.append(me.treeNode);
};

tree.prototype.getEachNodeMarkup = function (nodeObj, rootNode, selector) {
    var selectedNode, i, me = this;

    if (nodeObj.children) {

        if (!selector) {
            selectedNode = rootNode;
        } else {
            selectedNode = rootNode.find(selector);
        }

        nodeObj.class = nodeObj.class ? nodeObj.class : "";
        selectedNode.append($.parseHTML("<li name=" + nodeObj.label + " class='collapsed " + nodeObj.class + "'>" + "<label>" + nodeObj.label + "</label>" + "<ul></ul></li>"));
        selector = selector + " li[name=" + nodeObj.label + "] > ul";

        for (i = 0; i < nodeObj.children.length; i = i + 1) {
            me.getEachNodeMarkup(nodeObj.children[i], rootNode, selector);
        }

    } else {
        nodeObj.class = nodeObj.class ? nodeObj.class : "";
        rootNode.find(selector).append($.parseHTML("<li name=" + nodeObj.label + " class='" + nodeObj.class + "'>" + "<label>" + nodeObj.label + "</label>" + "</li>"));
    }
};

tree.prototype.getTree = function () {
    var component, me = this;

    for (component in me.dataObj) {
        if (me.dataObj.hasOwnProperty(component)) {
            me.getEachNodeMarkup(me.dataObj[component], me.treeNode, "");
        }
    }
    me.attachMarkUp();
    me.attachEvents();
    return me.treeNode;
};

Tree.css

树.css

.treeNode .collapsed > ul, .collapsed > li {
    display: none;
}

.treeNode .expanded > ul, .expanded > li {
    display: block;
}

testPage.js

测试页面.js

// the variable "config" is nothing but the config JSON defined initially. 
treeNode = new tree($('.treeContainer .tree'), config); 
treeNodeObj = treeNode.getTree();

Look at the example provided at https://jsfiddle.net/3s3k3zLL/

查看https://jsfiddle.net/3s3k3zLL/提供的示例

回答by talsibony

I was searching for general parent child element function and I saw these answers, and I took some pieces of code from here and there and made this function. I decided to share my code as an answer, in case someone like me will find this post when he is searching for a general parent child html element draw function:

我正在寻找一般的父子元素功能,我看到了这些答案,我从这里那里拿了一些代码并制作了这个功能。我决定分享我的代码作为答案,以防像我这样的人在搜索一般的父子 html 元素绘制函数时会发现这篇文章:

function drawRecElements(arr, html, elements) {
    if (typeof (html) === 'undefined') {
        var html = '';
    }
    if (typeof (elements) === 'undefined') {
        var elements = {child: '<li>', childClose: '</li>', parent: '<ul>', parentClose: '</ul>'};
    }
    if (typeof (arr) === 'string') {
        return elements.child + arr + elements.childClose;
    } else if (typeof (arr) === 'object') {
        for (i in arr) {
            if (typeof (arr[i]) === 'string') {
                html += elements.parent + elements.child + i + elements.childClose + elements.child + arr[i] + elements.childClose + elements.parentClose;
            } else if(typeof (i) === 'string' && (isNaN(i))){
                html += elements.parent + elements.child + i + elements.childClose + elements.child + drawRecElements(arr[i],'',elements) + elements.childClose + elements.parentClose;
            } else if (typeof (arr[i]) === 'object') {
               html = drawRecElements(arr[i], html,elements);
            }
        }
    }
    return html;
}

https://jsfiddle.net/kxn442z5/1/

https://jsfiddle.net/kxn442z5/1/