Create a UL Sub Menu Based on the Structure of the URLs of Menu Items

I have an array of menu items, each of which contains a name and a URL, for example:

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 try to do this as an unordered list with a nested UL structure that follows the URL structure, for example:

 <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 the examples I've seen start with a data structure that reflects the parent-child relationship (e.g. xml or JSON), but it is very difficult for me to get this out of the URL and use it to render the new structure.

If someone could direct me in the right direction how to do this using jQuery, I would really appreciate it. I understand that I probably need to use some recursive functions or maybe jQuery templates, but for me all this is a bit new. Thanks

+6
source share
6 answers

I think the best solution is, firstly, to convert your data structure into a tree with parent / child relationships. Then visualization of this structure will be easier, since UL itself has a tree structure.

You can convert menu items using these two 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 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 that you already have html rendering for trees, right? If you need more help, let us know!

+6
source

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/

+2
source

Although I like the gilly3 script, the script creates a list with the different nesting of the <li> and <ul> elements that was originally set. Therefore, instead of

 <li><a href="/store">Store</a> <ul> <li><a href="/store/travel">Travel</a></li> ... </ul> </li> 
He produces
 <li><a href="/store">Store</a> </li> <ul> <li><a href="/store/travel">Travel</a></li> ... </ul> 
This can lead to incompatibilities for utilities or frameworks working with such a generated menu, and the creation of an interactive menu with animations (for example, superfish.js). So I updated 12 lines of 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/

+2
source

This is not in jQuery, but maybe this can help. I designed this after searching the Internet to do exactly what you want.

http://www.chapleau.info/article/ArrayofUrlsToASitemap.html

0
source

try something like this.

 function Directory(parentNode) { //Structure for directories. Subdirectories container as a generic object, initially empty this.hasSubdirectories = false; this.subdirectories = {}; //Render in steps. Until subdirectories or a link are added, all it needs is an LI and a blank anchor this.nodeLi = document.createElement("li"); parentNode.appendChild(this.nodeLi); this.nodeA = document.createElement("a"); this.nodeLi.appendChild(this.nodeA); //if a subdirectory is added, this.nodeUl will be added at the same time. } Directory.prototype.setLabel = function (sLabel) { this.nodeA.innerHTML = sLabel; } Directory.prototype.setLink = function (sLink) { this.nodeA.href = sLink; } Directory.prototype.getSubdirectory = function (sPath) { //if there were no previous subdirectories, the directory needs a new UL node. if (!this.hasSubdirectories) { this.nodeUl = document.createElement("ul"); this.nodeLi.appendChild(this.nodeUl); this.hasSubdirectories = true; } //split the path string into the base directory and the rest of the path. var r = /^\/?(?:((?:\w|\s|\d)+)\/)(.*)$/; var path = r.exec(sPath); //if the desired path is in a subdirectory, find or create it in the subdirectories container. var subDirName = path[1] || path[2]; var subDir; if (this.subdirectories[subDirName] === undefined) this.subdirectories[subDirName] = new Directory(this.nodeUl); subDir = this.subdirectories[subDirName]; if (path[1] && path[2]) { return subDir.getSubdirectory(path[2]); } else { return subDir; } } function main(whichNode, aMenuItems) { //whichNode is the node that is to be the parent of the directory listing. //aMenuItems is the array of menu items. var i; var l = aItems.length; var topDir = new Directory(whichNode); //for each menu item, add a directory and set its properties. var dirToAdd; for (i = 0; i < l; i++) { dirToAdd = topDir.getSubdirectory(aMenuItems[i].url); dirToAdd.setLabel(aMenuItems[i].name); dirToAdd.setLink(aMenuItems[i].url); } //and that it. } 

how it works?

0
source

Or perhaps a complete jQuery plugin http://jsfiddle.net/9FGRC/

(EDIT)

Upgrade to the previous version http://jsfiddle.net/9FGRC/1/

This version supports the following case.

 var menuItems = [ { name : "Store", url : "/store" }, { name : "Cook Books", url : "/store/healthy-eating/cook-books" }, { name : "Single Meal Gifts", url : "/store/healthy-eating/single-meal-gifts" } ] 

Since it is skipped

  { name : "Healthy Eating", url : "/store/healthy-eating" }, 

It produces the following html

 <ul> <li><a href="/store">Store</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> </ul> 

I think this is not so, but it may be useful to someone

0
source

Source: https://habr.com/ru/post/895500/


All Articles