我正在尝试使用以下JSON数据在递归内部函数中创建以下类似的结构并没有太多运气,真的需要一些帮助,所以如果有人可以协助请做。提前谢谢。
<ul>
<li></li>
<li>
<a href=""></a>
<div>
<ul>
<li>
<a href=""></a>
<div>
....etc
</div>
</li>
</ul>
</div>
</li>
</ul>
我使用的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}
]
}
我创建的代码(不完整,这是我需要帮助的脑筋急转弯)是:
$(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
});
使用了集成代码,感谢个人提供了足够接近的答案 - 虽然会研究其他人。
$.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();
}
答案 0 :(得分:13)
您可以尝试我刚刚编写的这个递归函数:
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;
}
它返回菜单的html,因此请使用它:var html = buildList(JSON.menu, false);
我相信它更快,因为它是纯JavaScript,并且它不会为每次迭代创建文本节点或DOM元素。完成后,只需在结束时拨打.innerHTML
或$('...').html()
,而不是立即为每个菜单添加HTML。
JSFiddled:http://jsfiddle.net/remibreton/csQL8/
答案 1 :(得分:6)
制作两个函数makeUL
和makeLI
。 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");
}
需要适应您的需求,但您明白了。
答案 2 :(得分:2)
此解决方案使用单个递归函数。我使用Array
map()
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
}]
};
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
&#13;
这是一种更具活力的方法。您可以选择列表项的呈现方式以及子属性的内容。 mapFunc
参数是一个回调函数,可让您访问当前子节点及其父节点。
mapFunc
的范围是项目。因此,您可以使用item
以及this
来引用所述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;
}));
}
答案 3 :(得分:1)
<强>代码:强>
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 );
}
<div id="test"></div>
li ul ul li {
padding-left: 10px;
}
li ul ul ul {
padding: 0px;
}
答案 4 :(得分:1)
这就像是一个从JSON配置递归生成UL / LI的完整解决方案,它为每个节点提供可自定义的类,并支持每个节点的展开和折叠事件。这只提供了一个基本的工作模型,您可以根据自己的需要进行扩展和定制。
找到了这个答案示例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代码:
<!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();
提供的示例
答案 5 :(得分:1)
Pure ES6
var foo=(arg)=>
`<ul>
${arg.map(elem=>
elem.sub?
`<li>${foo(elem.sub)}</li>`
:`<li>${elem.name}</li>`
)}
</ul>`
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)
您的结果&#39;将是有效的HTML
答案 6 :(得分:0)
我正在搜索一般的父子元素功能,我看到了这些答案, 我从这里和那里拿了一些代码并完成了这个功能。 我决定分享我的代码作为答案, 如果像我这样的人在搜索一般父子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;
}
答案 7 :(得分:0)
对旧答案的编辑使这个问题重新出现,我认为在现代 JS 中,这比上面的许多答案更容易。
这是一种解决方案,使用模板字符串和参数解构:
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>
这里的大部分复杂性只是简单地处理具有 sub
-arrays 和不具有 name
/link
属性的节点和具有 id
-arrays 的节点的不同格式只是一个 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>'
属性。如果输入更一致一点,这会更简单。尽管如此,这并不是一个可怕的版本。如果您喜欢使用基于字符串的 DOM 解决方案,那么它相当干净。
请注意,虽然现代功能使解决方案更简洁,但我们可以在 ES5 中做同样的事情:
''