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

前端 未结 7 750
暖寄归人
暖寄归人 2020-12-30 12:32

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 an

相关标签:
7条回答
  • 2020-12-30 12:57

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

    $(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.

    The scope of the mapFunc is the item. So you could use item as well as this to refer to said 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;
        }));
    }
    
    0 讨论(0)
  • 2020-12-30 13:00

    Fiddle

    Code:

    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

    li ul ul li {
        padding-left: 10px;
    }
    
    li ul ul ul {
        padding: 0px;
    }
    
    0 讨论(0)
  • 2020-12-30 13:03

    Make two functions makeUL and makeLI. makeUL calls makeLI on each element, and makeLI calls makeUL if there's sub elements:

    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/

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

    0 讨论(0)
  • 2020-12-30 13:05

    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:

    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/

    0 讨论(0)
  • 2020-12-30 13:06

    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.

    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

    Example JSON config file:

    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:

    <!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

    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

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

    testPage.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/

    0 讨论(0)
  • 2020-12-30 13:08

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

    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 .innerHTML or $('...').html() at the end when you're done instead of adding HTML immediately for every menu.

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

    0 讨论(0)
提交回复
热议问题