可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
<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:
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();
}
回答1:
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/
回答2:
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.
回答3:
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;
}));
}
回答4:
Pure ES6
var foo=(arg)=>
`<ul>
${arg.map(elem=>
elem.sub?
`<li>${foo(elem.sub)}</li>`
:`<li>${elem.name}</li>`
)}
</ul>`
JSON example
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
回答5:
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 + ': "' + data[key] + '"</li >' );
}
};
htmlRetStr += '</ul >';
return( htmlRetStr );
}
HtML
<div id="test"></div>
CSS
li ul ul li {
padding-left: 10px;
}
li ul ul ul {
padding: 0px;
}
回答6:
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/
回答7:
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/