简体   繁体   English

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

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

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

<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();

}

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.我相信它更快,因为它使用纯 JavaScript,并且不会为每次迭代创建文本节点或 DOM 元素。 Just call .innerHTML or $('...').html() at the end when you're done instead of adding HTML immediately for every menu.完成后只需在最后调用.innerHTML$('...').html()而不是立即为每个菜单添加 HTML。

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

Make two functions makeUL and makeLI .制作两个函数makeULmakeLI makeUL calls makeLI on each element, and makeLI calls makeUL if there's sub elements: makeUL调用makeLI每个元素,并makeLI调用makeUL如果有sub元素:

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.需要根据您的需求进行调整,但您明白了。

Pure ES6纯 ES6

var foo=(arg)=>
`<ul>
${arg.map(elem=>
    elem.sub?
        `<li>${foo(elem.sub)}</li>`
        :`<li>${elem.name}</li>`
    ).join('')}
</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

This solution uses a single recursive function.此解决方案使用单个递归函数。 I simplified logic by using Array 's map() prototype function.我通过使用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 mapFunc paramater is a callback that gives you access to the current child node and its parent. mapFunc参数是一个回调,可让您访问当前子节点及其父节点。

The scope of the mapFunc is the item. mapFunc的范围是项目。 So you could use item as well as this to refer to said item .所以你可以使用itemthis来引用所述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;
    }));
}

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 html

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

CSS CSS

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

li ul ul ul {
    padding: 0px;
}

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.这就像一个从 JSON 配置递归生成 UL/LI 的完整解决方案,它为每个节点提供可自定义的类,并支持每个节点的展开和折叠事件。 This provides just a basic working model, from which you ll be able to expand and customize to your needs.这仅提供了一个基本的工作模型,您可以从中扩展和定制您的需求。

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/提供的示例

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/

An edit to an old answer brought this question back up, and I think in modern JS this becomes easier than many answers above.对旧答案的编辑使这个问题重新出现,我认为在现代 JS 中,这比上面的许多答案更容易。

Here is one solution, using template strings and parameter destructuring:这是一种解决方案,使用模板字符串和参数解构:

 const buildMenu = (nodes) => `<ul>${nodes.map( ({id, name, link, sub}) => sub ? `<li>${name ? `<a href="${link}">${name}</a>` : id}<div>${buildMenu(sub)}</div></li>` : `<li>${id || `<a href="${link}">${name}</a>`}</li>` ).join('')}</ul>` 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}]} const tree = buildMenu(JSON.menu); document.getElementById('output').innerHTML = tree
 <div id="output"></div>

Most of the complexity here is simply in handling different formats for nodes that have sub -arrays and those that don't and for those with name / link properties and those with just an id property.这里的大部分复杂性只是处理具有sub数组的节点和没有sub数组的节点以及具有name / link属性的节点和仅具有id属性的节点的不同格式。 If the input was a little more consistent, this would be simpler still.如果输入更一致一点,这会更简单。 Nonetheless, this is not a horrible version.尽管如此,这并不是一个可怕的版本。 If you like to work with string-based DOM solutions, it's fairly clean.如果您喜欢使用基于字符串的 DOM 解决方案,它是相当干净的。

Note that while modern features make the solution cleaner, we could do the same thing in ES5:请注意,虽然现代功能使解决方案更简洁,但我们可以在 ES5 中做同样的事情:

const buildMenu = (nodes) =>
  '<ul>' + nodes.map (
    (node) => node.sub
      ? '<li>' + (node.name ? '<a href="' + node.link + '">' + node.name + '</a>' : node.id) + '<div>' + buildMenu(node.sub) + '</div></li>'
      : '<li>' + (node.id || '<a href="' + node.link + '">' + node.name + '</a>') + '</li>'
  ) .join ('') + '</ul>'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM