Skip to content Skip to sidebar Skip to footer

Create A Nested UL Menu Based On The URL Path Structure Of Menu Items

I have an array of menu items, each containing Name and URL like this: var menuItems = [ { name : 'Store', url : '/store' }, { nam

Solution 1:

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!


Solution 2:

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/


Solution 3:

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/


Solution 4:

It's not in jQuery, but maybe this could help. I developed this after seeking the web to do exactly what you want.

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


Solution 5:

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

(EDIT)

An update to previous version http://jsfiddle.net/9FGRC/1/

This version supports 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 there is skipped

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

It will produce 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 guess it won't be the case, but could be helpful to someone


Post a Comment for "Create A Nested UL Menu Based On The URL Path Structure Of Menu Items"