I have an array of menu items, each containing Name and URL like this:
var menuItems = [
{
name : "Store",
url : "/store"
},
{
name : "Travel",
url : "/store/travel"
},
{
name : "Gardening",
url : "/store/gardening"
},
{
name : "Healthy Eating",
url : "/store/healthy-eating"
},
{
name : "Cook Books",
url : "/store/healthy-eating/cook-books"
},
{
name : "Single Meal Gifts",
url : "/store/healthy-eating/single-meal-gifts"
},
{
name : "Outdoor Recreation",
url : "/store/outdoor-recreation"
},
{
name : "Hiking",
url : "/store/outdoor-recreation/hiking"
},
{
name : "Snowshoeing",
url : "/store/outdoor-recreation/hiking/snowshoeing"
},
{
name : "Skiing",
url : "/store/outdoor-recreation/skiing"
},
{
name : "Physical Fitness",
url : "/store/physical-fitness"
},
{
name : "Provident Living",
url : "/store/provident-living"
}
]
I've been trying with no success to render this as an unordered list with a nested UL structure that follows the URL path structure like so:
<ul>
<li><a href="/store">Store</a>
<ul>
<li><a href="/store/travel">Travel</a></li>
<li><a href="/store/gardening">Gardening</a></li>
<li><a href="/store/healthy-eating">Healthy Eating</a>
<ul>
<li><a href="/store/healthy-eating/cook-books">Cook Books</a></li>
<li><a href="/store/healthy-eating/single-meal-gifts">Single Meal Gifts</a></li>
</ul>
</li>
<li><a href="/store/outdoor-recreation">Outdoor Recreation</a>
<ul>
<li><a href="/store/outdoor-recreation/hiking">Hiking</a>
<ul>
<li><a href="/store/outdoor-recreation/hiking/snowshoeing">Snowshoeing</a></li>
</ul>
</li>
<li><a href="/store/outdoor-recreation/skiing">Skiing</a></li>
</ul>
</li>
<li><a href="/store/physical-fitness">Physical Fitness</a></li>
<li><a href="/store/provident-living">Provident Living</a></li>
</ul>
</li>
</ul>
All of the examples I've seen begin with a data structure that reflects the parent-child relationship (e.g. xml or JSON), but I'm having a very difficult time pulling this out of the URL and using it to render the new structure.
If anyone could please steer me in the right direction for how to do this using jQuery, I'd really appreciate it. I realize I probably need to use some recursive functions or maybe jQuery templates, but these things are still a bit new to me.
Thanks
I think the best solution is firstly to convert your data structure to a tree one, with parent/children relations. Render this structure will then be easier, as the UL itself has a tree structure.
You can convert menuItems using these couple of functions
// Add an item node in the tree, at the right position
function addToTree( node, treeNodes ) {
// Check if the item node should inserted in a subnode
for ( var i=0; i<treeNodes.length; i++ ) {
var treeNode = treeNodes[i];
// "/store/travel".indexOf( '/store/' )
if ( node.url.indexOf( treeNode.url + '/' ) == 0 ) {
addToTree( node, treeNode.children );
// Item node was added, we can quit
return;
}
}
// Item node was not added to a subnode, so it's a sibling of these treeNodes
treeNodes.push({
name: node.name,
url: node.url,
children: []
});
}
//Create the item tree starting from menuItems
function createTree( nodes ) {
var tree = [];
for ( var i=0; i<nodes.length; i++ ) {
var node = nodes[i];
addToTree( node, tree );
}
return tree;
}
var menuItemsTree = createTree( menuItems );
console.log( menuItemsTree );
The resulting menuItemsTree will be an object like this
[
{
"name":"Store",
"url":"/store",
"children":[
{
"name":"Travel",
"url":"/store/travel",
"children":[
]
},
{
"name":"Gardening",
"url":"/store/gardening",
"children":[
]
},
{
"name":"Healthy Eating",
"url":"/store/healthy-eating",
"children":[
{
"name":"Cook Books",
"url":"/store/healthy-eating/cook-books",
"children":[
]
},
{
"name":"Single Meal Gifts",
"url":"/store/healthy-eating/single-meal-gifts",
"children":[
]
}
]
},
{
"name":"Outdoor Recreation",
"url":"/store/outdoor-recreation",
"children":[
{
"name":"Hiking",
"url":"/store/outdoor-recreation/hiking",
"children":[
{
"name":"Snowshoeing",
"url":"/store/outdoor-recreation/hiking/snowshoeing",
"children":[
]
}
]
},
{
"name":"Skiing",
"url":"/store/outdoor-recreation/skiing",
"children":[
]
}
]
},
{
"name":"Physical Fitness",
"url":"/store/physical-fitness",
"children":[
]
},
{
"name":"Provident Living",
"url":"/store/provident-living",
"children":[
]
}
]
}
]
You mentioned you already have html renderer for trees, right? If you need further help let us know!
12 simple lines of code:
var rootList = $("<ul>").appendTo("body");
var elements = {};
$.each(menuItems, function() {
var parent = elements[this.url.substr(0, this.url.lastIndexOf("/"))];
var list = parent ? parent.next("ul") : rootList;
if (!list.length) {
list = $("<ul>").insertAfter(parent);
}
var item = $("<li>").appendTo(list);
$("<a>").attr("href", this.url).text(this.name).appendTo(item);
elements[this.url] = item;
});
http://jsfiddle.net/gilly3/CJKgp/
Although I like the script of gilly3 the script produces list with different element nesting of <li>
and <ul>
than was originally asked. So instead of
<li><a href="/store">Store</a>
<ul>
<li><a href="/store/travel">Travel</a></li>
...
</ul>
</li>
It produces
<li><a href="/store">Store</a>
</li>
<ul>
<li><a href="/store/travel">Travel</a></li>
...
</ul>
This may cause incompatibilities for utilities or frameworks working with such generated menu and producing interactive menu with animation (e.g. superfish.js).
So I updated the 12 lines script
var rootList = $("<ul>").appendTo("body");
var elements = {};
$.each(menuItems, function() {
var parent = elements[this.url.substr(0, this.url.lastIndexOf("/"))];
var list = parent ? parent.children("ul") : rootList;
if (!list.length) {
list = $("<ul>").appendTo(parent);
}
var item = $("<li>").appendTo(list);
$("<a>").attr("href", this.url).text(this.name).appendTo(item);
elements[this.url] = item;
});
http://jsfiddle.net/tomaton/NaU4E/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With