简体   繁体   中英

Multi-Level Menu

I've been scratching my head on how to get this dynamic menu working in a cleaner way than I currently do, I currently receive a list of menu items as a flat XML structure.

I've created an object below illustrating how the data will look inside an object, it has a parent-child relationship for each menu item (I've indented for ease of reading)

let Menu = [
  {
    DisplayName  : "Menu1",
    href         : "Menu1.aspx",
    MenuID       : "1",
    ParentMenuID : "?",
    Position     : "1",
    UserTypeCode : "99"
  },
      {
        DisplayName  : "Menu-1-1",
        href         : "Menu1.aspx",
        MenuID       : "2",
        ParentMenuID : "1",
        Position     : "1",
        UserTypeCode : "99"
      },
      {
        DisplayName  : "Menu-1-2",
        href         : "Menu1.aspx",
        MenuID       : "3",
        ParentMenuID : "1",
        Position     : "2",
        UserTypeCode : "99"
      },
  {
    DisplayName  : "Menu2",
    href         : "Menu2.aspx",
    MenuID       : "4",
    ParentMenuID : "?",
    Position     : "2",
    UserTypeCode : "99"
  },
      {
        DisplayName  : "Menu2-1",
        href         : "Menu1.aspx",
        MenuID       : "5",
        ParentMenuID : "4",
        Position     : "1",
        UserTypeCode : "99"
      },
      {
        DisplayName  : "Menu2-2",
        href         : "Menu1.aspx",
        MenuID       : "6",
        ParentMenuID : "4",
        Position     : "2",
        UserTypeCode : "99"
      },
          {
            DisplayName  : "Menu2-2-1",
            href         : "Menu1.aspx",
            MenuID       : "7",
            ParentMenuID : "6",
            Position     : "1",
            UserTypeCode : "99"
          }
];

I would like to know how I could dynamically create the menu object (using a recursive function I believe would be ideal) without my current somewhat messy solution of requiring a different function for each level of my menu, Here is what my code looks like at the minute, It is limited to only 3 levels of menu's

What I desire is a nice function where it will accept any number of menu levels

function createRoot(){

    // Initially map all root elements
    Menu.map((item, index) => {

        if (item.ParentMenuID === "?"){

            // If the the next menu item is a child of this root
            if (Menu[index+1].ParentMenuID === item.MenuID){

                // Generate boilerplate dropdown code + then retrieve it's children
                htmlStr += `<li class="dropdown-submenu"><a class="test" data-menuID="`+item.MenuID+`" data-menuitem="`+item.href+`" href="#">`+item.DisplayName+`<span class="caret"></a>`
                    htmlStr += `<ul class="dropdown-menu">`
                        GetLevel1(item);
                    htmlStr += `</ul>`
                htmlStr += `</li>`

            // Otherwise it's just a normal menu item
            } else {
                htmlStr += `<li><a class="test" data-menuID="`+item.MenuID+`" data-menuitem="`+item.href+`" href="#">`+item.DisplayName+`</a></li>`
            }
        }


    });

    $('#user-menu-list').html(htmlStr);

      // Setup event on list items
      $('.dropdown-submenu a.test').on("click", function(e){
        $(this).next('ul').toggle();
        e.stopPropagation();
        e.preventDefault();
      });
}

function GetLevel1(item){

    Menu.map((subItem, index) => {

        if (index < Menu.length-1){
        console.log(index);
            // If the current item is a child of the root
            if (subItem.ParentMenuID === item.MenuID){
                // If the the next item is a child of this root
                if (Menu[index+1].ParentMenuID === subItem.MenuID){
                    htmlStr += `<li class="dropdown-submenu"><a class="test" data-menuID="`+subItem.MenuID+`" data-menuitem="`+subItem.href+`" href="#">`+subItem.DisplayName+`<span class="caret"></a>`
                        htmlStr += `<ul class="dropdown-menu">`
                            GetLevel2(subItem);
                        htmlStr += `</ul>`
                    htmlStr += `</li>`
                } else {
                    htmlStr += `<li><a class="test" data-menuID="`+subItem.MenuID+`" data-menuitem="`+subItem.href+`" href="#">`+subItem.DisplayName+`</a></li>`
                }
            }

        }


    });
}

function GetLevel2(item){

    Menu.map((subItem, index) => {

        console.log("INDEX: "+index);
        // If the current item is a child of the root
        if (subItem.ParentMenuID === item.MenuID){
            htmlStr += `<li><a class="test" data-menuID="`+subItem.MenuID+`" data-menuitem="`+subItem.href+`" href="#">`+subItem.DisplayName+`</a></li>`
        }
    });
}

createRoot();

Here is a full pastebin link with my menu working at the minute: http://pastebin.com/ektBQ7kd

Any help would be much appreciated!

My recursive abilities are a bit rusty this can be improved, I would do something like this, First go over all the menu, add children menus to their parents.

After phase one complete and I have menus and submenus in one object, you can do recursive again and build the menu.

Something like this:

 var Menu = [{ DisplayName: "Menu1", href: "Menu1.aspx", MenuID: "1", ParentMenuID: "?", Position: "1", UserTypeCode: "99" }, { DisplayName: "Menu-1-1", href: "Menu1.aspx", MenuID: "2", ParentMenuID: "1", Position: "1", UserTypeCode: "99" }, { DisplayName: "Menu-1-2", href: "Menu1.aspx", MenuID: "3", ParentMenuID: "1", Position: "2", UserTypeCode: "99" }, { DisplayName: "Menu2", href: "Menu2.aspx", MenuID: "4", ParentMenuID: "?", Position: "2", UserTypeCode: "99" }, { DisplayName: "Menu2-1", href: "Menu1.aspx", MenuID: "5", ParentMenuID: "4", Position: "1", UserTypeCode: "99" }, { DisplayName: "Menu2-2", href: "Menu1.aspx", MenuID: "6", ParentMenuID: "4", Position: "2", UserTypeCode: "99" }, { DisplayName: "Menu2-2-1", href: "Menu1.aspx", MenuID: "7", ParentMenuID: "6", Position: "1", UserTypeCode: "99" }]; var main = []; function getSiblins(currentNode, currentId) { if (!currentId) { currentNode.children = currentNode.children || new Array(); return getSiblins(currentNode, currentNode.MenuID) } else { for (var j = 0; j < Menu.length; j++) { if (Menu[j].ParentMenuID == currentId) { currentNode.children.push(Menu[j]); } } } if (currentNode.ParentMenuID == "?") { //root menu main.push(currentNode); } } for (var i = 0; i < Menu.length; i++) getSiblins(Menu[i]); function prepareMenu(currentMenu) { var appendHtml = "<li>" + currentMenu.DisplayName + "</li>" for (var i = 0; i < currentMenu.children.length; i++) { appendHtml = appendHtml + "<ul class='children'>" + prepareMenu(currentMenu.children[i]) + "</ul>"; } return appendHtml; } var menuHtml = ""; for (var j = 0; j < main.length; j++) { menuHtml += prepareMenu(main[j]); } document.getElementById("finalMenu").innerHTML = menuHtml; 
 .children li{ color: blue; } .children .children li { color: green; } 
 <ul id="finalMenu"> </ul> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM