Create a nested UL menu based on the URL path structure of menu items

前端 未结 6 595

I have an array of menu items, each containing Name and URL like this:

var menuItems = [  
    {  
        name : \"Store\",  
        url : \"/store\"  
          


        
相关标签:
6条回答
  • 2021-01-05 08:50

    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!

    0 讨论(0)
  • 2021-01-05 08:56

    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's it.
    }
    

    how's that work?

    0 讨论(0)
  • 2021-01-05 08:57

    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

    0 讨论(0)
  • 2021-01-05 09:01

    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

    0 讨论(0)
  • 2021-01-05 09:14

    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/

    0 讨论(0)
  • 2021-01-05 09:15

    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/

    0 讨论(0)
提交回复
热议问题