代码之家  ›  专栏  ›  技术社区  ›  Ren Jitsm

当其他菜单打开时,我如何折叠Vanilla JS多级菜单

  •  0
  • Ren Jitsm  · 技术社区  · 3 年前

    我使用纯JS创建了一个多级基本导航菜单,并试图在打开其他子菜单时找到一个折叠子菜单的逻辑。我用foreach循环所有有子菜单的菜单。但不知道如何在点击菜单时检测其他菜单。有人能帮我吗。提前感谢!

    /* toggle click function for submenus */
    slideToggle=(el)=> {
    let cs = window.getComputedStyle(el).display;
    if(cs==="none") {
        el.style.display="block";
    }
    else {
        el.style.display="none";
    }
    }
    
    /* detecting menus which has submenus */
    submenuDetect=()=> {
        let li = document.querySelectorAll(".main-navigation-content > ul li");
    
        /* adding a class "has-submenu" for menu which has sublevel menu  */
        li.forEach((item)=>{
            let ul = item.querySelector("ul");
            if(ul){
                item.classList.add("has-submenu");
                ul.classList.add("submenu-content");
    
                /* adding icon */
                let iconElement = document.createElement("span");
                iconElement.setAttribute("class","down-arrow-icon submenu-trigger");
                item.querySelector("a").insertAdjacentElement("afterend", iconElement);
            }
        });
    }
    
    /* assign toggle function to menu which has submenus */
    submenuToggle=()=>{
       /* find all menu which has submenus and making toggle */ 
       let submenuTrigger = document.querySelectorAll(".has-submenu > a");
       submenuTrigger.forEach((item)=>{
           item.addEventListener("click",(e)=>{
               e.preventDefault();
            let submenuContent = e.target.parentElement.querySelector('.submenu-content');
            slideToggle(submenuContent);
           })
       }); 
    }
    
    /* calling submenu detect function and slidetoggle function */
    submenuDetect();
    submenuToggle();
    body {font-family: Arial, Helvetica, sans-serif;;}
    .main-navigation-content ul {list-style: none;margin:0;padding:0;transition:all 0.35s ease;}
    .main-navigation-content ul li {border:1px solid #cdcdcd;margin-bottom: -1px;/* margin-left: -1px; *//* margin-right: -1px; */background: #f5f5f5;/* margin: -1px; *//* height: 2.5rem; */}
    .main-navigation-content ul li a {text-decoration: none;line-height: 2.5rem;padding-left: .5rem;color: #555;display: flex;align-items: center;justify-content: space-between;position: relative;z-index: 1;padding-right: .5rem;transition:all 0.35s ease;/* width: 100%; */height: 2.5rem;flex-grow: 1;}
    .main-navigation-content ul li ul li {margin-left: -1px;margin-right: -1px;}
    .down-arrow-icon {width: 2.5rem;display: flex;height: 2.5rem;align-items: center;justify-content: center;position: absolute;z-index: 0;right: 0;top: 0;cursor: pointer;margin-right: 0;line-height: 2.5rem;/* background: red; */}
    .down-arrow-icon:after { content:"";    width: .03rem;    height: .03rem;    background-color: transparent;    border: solid black;    border-width: 0 2px 2px 0;    display: inline-block;    padding: 3px;    transform: rotate(45deg);    -webkit-transform: rotate(45deg);    position: absolute;    top: 35%;}    
    .navigation-master-wrapper {border: 1px solid #cdcdcd;   width: auto;background: #f9f9f9;height: 500px;overflow-y: auto;}
    .main-navigation {position: relative; height: auto;}
    .main-navigation-trigger {margin-left: auto;position: relative;cursor: pointer;left: auto;right: .25rem;display: inline-block;padding: 0;font-size: 12px;top: 0;z-index: 100;margin-bottom: .5rem;display: none;}
    .main-navigation-content {    display:none;}
    .main-navigation-content > ul { list-style: none;padding: 0;margin: 0;}
    .main-navigation-content > ul li { position: relative;}
    .main-navigation-content .submenu-content {display: none;}
    @media only screen and (min-width:800px) {
    .main-navigation-content > ul li .submenu-content li a {padding-left:2rem;}
    .main-navigation-content > ul li .submenu-content li ul li a {padding-left:3rem}
    .main-navigation-content > ul li  .submenu-content {margin-top:1px;background: #fff;}
    .main-navigation-content > ul li  .submenu-content li ul {margin-top:0;}
    .main-navigation-content {display:block !important;width: 20rem;}
    .main-navigation-content > ul li > ul {top:100%;}   
    .main-navigation-content > ul li {position: initial;}
    .main-navigation-content > ul > li {position: relative;}
    .main-navigation-content > ul > li a {/* padding-left: 1rem; *//* padding-right: 1rem; */}
    .main-navigation-content > ul li > ul li {position:relative;background: #fff;}
    .main-navigation-content > ul li ul {transition:all 0.35s ease;}
    }
    <div class="main-navigation">
        <div class="main-navigation-trigger">
            <div class="main-navigation-trigger-inner"></div>
        </div>
        <div class="main-navigation-content">
            <ul>
                <li><a href="">Home</a></li>
                <li><a href="">About Us</a>
                    <ul>
                        <li><a href="">History and Foundation</a></li>
                        <li><a href="">Company Overview</a></li>
                        <li><a href="">Working Strategy</a>
                            <ul>
                                <li><a href="">Employer Profile</a></li>
                                <li><a href="">Manpower Allocation</a></li>
                                <li><a href="">Marketing Strategy</a></li>
                            </ul>
                        </li>
                    </ul>
                </li>
                <li><a href="">Services</a>
                    <ul>
                        <li><a href="">Graphic Designing</a></li>
                        <li><a href="">Web Designing</a></li>
                        <li><a href="">App Development</a>
                            <ul>
                                <li><a href="">Android</a></li>
                                <li><a href="">IOS</a></li>
                                <li><a href="">Hybrid</a></li>
                            </ul>
                        </li>
                    </ul>
                </li>
                <li><a href="">Contact</a></li>
            </ul>
        </div>
    </div>
    0 回复  |  直到 3 年前
        1
  •  1
  •   Mister Jojo    3 年前

    我试图理解你的代码以改进它,但几个小时后就放弃了这个想法。然而,为了不放弃自己,这里有一个变通方法,你可以使用。。。
    我希望我的代码至少能在将来帮助您简化您的代码。

    const menu = 
      [ { lib: 'Home',                        link: '#Home'      } 
      , { lib: 'About Us',                    link: '#About',    sub: 
          [ { lib: 'History and Foundation',  link: '#History'   } 
          , { lib: 'Company Overview',        link: '#Company'   } 
          , { lib: 'Working Strategy',        link: '#Working',  sub: 
              [ { lib: 'Employer Profile',    link: '#Employer'  } 
              , { lib: 'Manpower Allocation', link: '#Manpower'  } 
              , { lib: 'Marketing Strategy',  link: '#Marketing' } 
        ] } ] } 
      , { lib: 'Services',                    link: '#Services', sub: 
          [ { lib: 'Graphic Designing',       link: '#Graphic'   } 
          , { lib: 'Web Designing',           link: '#Web'       } 
          , { lib: 'App Development',         link: '#App',      sub: 
              [ { lib: 'Android',             link: '#Android'   } 
              , { lib: 'IOS',                 link: '#IOS'       } 
              , { lib: 'Hybrid',              link: '#Hybrid'    } 
        ] } ] } 
      , { lib: 'Contact',                     link: '#Contact'   } 
      ]
    
    const navMenu = document.querySelector('div.main-nav')
    
    const OpenMenus = [] // to memorize DOM (li.show) menus opened
    
    function makeMenu( nav, jso )
      {
      let eUL = nav.appendChild( document.createElement('ul') )
    
      for (const row of jso) 
        {
        let eLI = eUL.appendChild( document.createElement('li') )
          , lnk = eLI.appendChild( document.createElement('a') )
          ;
        lnk.textContent = row.lib 
        lnk.href        = row.link 
        if (!!row.sub)
          {
          eLI.className = 'submenu'
          makeMenu(eLI,row.sub)
          }
        }
      }
    makeMenu( navMenu, menu )
    
    navMenu.addEventListener('click', (e)=>
      {
      if (!e.target.matches('li.submenu > a')) return
      e.preventDefault()
    
      let level = 0
        , eLI   = e.target.closest('li')
        , eUL   = e.target.closest('ul')
        , mOpen = eLI.classList.toggle('show')
        ;
      for(;;level++) // get menu Level
        {
        eUL = eUL.parentElement.closest('ul')
        if (!eUL || !eUL.matches('div.main-nav ul')) break
        }
      for(let i = OpenMenus.length; (--i) > level;) // close all < sub levels
        {
        OpenMenus.pop().classList.remove('show')
        }
      if (OpenMenus[level] && OpenMenus[level] != eLI )
        OpenMenus.pop().classList.remove('show')
    
      if (mOpen)  OpenMenus.push(eLI)  // memorize adding menu opened
      else        OpenMenus.pop()     // or remove it. 
      })
    body { background: steelblue; font-family: Arial, Helvetica, sans-serif; }
    
    .main-nav * { 
      box-sizing : border-box;
      }
    .main-nav {
      width         : 20rem;
      border-bottom : 1px solid #fbfbfb;
      }
    .main-nav ul { 
      list-style : none;
      margin     : 0;
      padding    : 0;
      }
    .main-nav a { 
      display         : block;
      width           : 100%;
      border          : 1px solid #fbfbfb;
      border-bottom   : none;
      background      : #c7c7c7;
      text-decoration : none;
      line-height     : 2.5rem;
      padding         : 0 .5rem;
      color           : #2a2a2a;
      }
    .main-nav a:hover {
      background: #ebebeb;
      }
    .main-nav ul ul a    { padding-left:2rem; background: #d5d5d5; }
    .main-nav ul ul ul a { padding-left:3rem; background: #e1e1e1; }
    
    .main-nav li.submenu > ul { 
      max-height : 0;
      transition : max-height 0.25s ease-out;
      overflow   : hidden;
      }
    .main-nav li.submenu > a::after {
      display    : block;
      float      : right;
      content    : '\276F'; /*'\1405'; */
      transition : 180ms;
      transform  : rotate(90deg);
      }
    .main-nav li.submenu.show > ul {
      max-height : 500px;
      transition : max-height 0.35s ease-in;
      }
    .main-nav li.submenu.show > a  {
      padding-right    : .7rem; /* because '\276F' is not symetric ! */
      }
    .main-nav li.submenu.show > a::after {
      transform  : rotate(-90deg);
      }
    <div class="main-nav"></div>
        2
  •  1
  •   Ren Jitsm    3 年前

    你好,朋友Bitski和先生Jojo,最后我找到了一个解决方案,它是如下。请看一看,让我知道你对我如何改进它的评论。谢谢你们花了宝贵的时间给我。谢谢你们!!!

    /* toggle click function for submenus */
    slideToggle = (el) => {
        let cs = window.getComputedStyle(el).display;
        if (cs === "none") {
            el.style.display = "block";
        }
        else {
            el.style.display = "none";
        }
    }
    
    /* detecting menus which has submenus */
    submenuDetect=()=> {
        let li = document.querySelectorAll(".main-navigation-content > ul li");
        /* adding a class "has-submenu" for menu which has sublevel menu  */
        li.forEach((item)=>{
            let ul = item.querySelector("ul");
            if(ul){
                item.classList.add("has-submenu");
                ul.classList.add("submenu-content");
                /* adding icon */
                let iconElement = document.createElement("span");
                iconElement.setAttribute("class","down-arrow-icon");
                item.querySelector("a").insertAdjacentElement("afterend", iconElement);
            }
        });
    }
    
    let secondlevelToggle=()=>{
        let secondlevelSubmenus = document.querySelectorAll(".submenu-content .has-submenu > a");
            secondlevelSubmenus.forEach((el)=>{
                el.addEventListener("click",(e)=>{
                    e.preventDefault();
                    e.stopPropagation();
                    let submenuContent = e.target.parentElement.querySelector(".submenu-content");
                    let submenuContentCS = window.getComputedStyle(submenuContent).display;
                    if(submenuContentCS==="none") {
                        submenuContent.style.display="block";
                    }
                    else {
                        submenuContent.style.display="none";
                    }
                });
            });
    }
    
    /* assign toggle function to menu which has submenus */
    submenuToggle = () => {
        /* find all menu which has submenus and making toggle */
        let submenuTrigger = document.querySelectorAll(".has-submenu > a");
        submenuTrigger.forEach((item) => {
            item.addEventListener("click", (e) => {
                e.stopPropagation();
                e.preventDefault();
                let allSubmenuContent = document.querySelectorAll(".main-navigation-content > ul > li > .submenu-content");
                allSubmenuContent.forEach((el) => {
    
                    /* Current Submenu */
                    let referingSubmenu = e.currentTarget.parentElement.querySelector(".main-navigation-content > ul > .has-submenu > .submenu-content");
                    let referingSubmenuCS = window.getComputedStyle(referingSubmenu).display;
                    console.log(referingSubmenuCS);
                    if (referingSubmenuCS === "none") {
                        allSubmenuContent.forEach((elem) => {
                            if (window.getComputedStyle(elem).display === "block") {
                                elem.style.display = "none";
                            }
                        })
                        referingSubmenu.style.display = "block";
                    }
                    else {
                        referingSubmenu.style.display = "none";
                    }
                });
            });
        });
    }
    
    submenuDetect();
    submenuToggle();
    secondlevelToggle();
    body {font-family: Arial, Helvetica, sans-serif;;}
    .main-navigation-content ul {list-style: none;margin:0;padding:0;transition:all 0.35s ease;}
    .main-navigation-content ul li {border:1px solid #cdcdcd;margin-bottom: -1px;/* margin-left: -1px; *//* margin-right: -1px; */background: #f5f5f5;/* margin: -1px; *//* height: 2.5rem; */}
    .main-navigation-content ul li a {text-decoration: none;line-height: 2.5rem;padding-left: .5rem;color: #555;display: flex;align-items: center;justify-content: space-between;position: relative;z-index: 1;padding-right: .5rem;transition:all 0.35s ease;/* width: 100%; */height: 2.5rem;flex-grow: 1;}
    .main-navigation-content ul li ul li {margin-left: -1px;margin-right: -1px;}
    .down-arrow-icon {width: 2.5rem;display: flex;height: 2.5rem;align-items: center;justify-content: center;position: absolute;z-index: 0;right: 0;top: 0;cursor: pointer;margin-right: 0;line-height: 2.5rem;/* background: red; */}
    .down-arrow-icon:after { content:"";    width: .03rem;    height: .03rem;    background-color: transparent;    border: solid black;    border-width: 0 2px 2px 0;    display: inline-block;    padding: 3px;    transform: rotate(45deg);    -webkit-transform: rotate(45deg);    position: absolute;    top: 35%;}    
    .navigation-master-wrapper {border: 1px solid #cdcdcd;   width: auto;background: #f9f9f9;height: 500px;overflow-y: auto;}
    .main-navigation {position: relative; height: auto;}
    .main-navigation-trigger {margin-left: auto;position: relative;cursor: pointer;left: auto;right: .25rem;display: inline-block;padding: 0;font-size: 12px;top: 0;z-index: 100;margin-bottom: .5rem;display: none;}
    .main-navigation-content {    display:none;}
    .main-navigation-content > ul { list-style: none;padding: 0;margin: 0;}
    .main-navigation-content > ul li { position: relative;}
    .main-navigation-content .submenu-content {display: none;}
    @media only screen and (min-width:800px) {
    .main-navigation-content > ul li .submenu-content li a {padding-left:2rem;}
    .main-navigation-content > ul li .submenu-content li ul li a {padding-left:3rem}
    .main-navigation-content > ul li  .submenu-content {margin-top:1px;background: #fff;}
    .main-navigation-content > ul li  .submenu-content li ul {margin-top:0;}
    .main-navigation-content {display:block !important;width: 20rem;}
    .main-navigation-content > ul li > ul {top:100%;}   
    .main-navigation-content > ul li {position: initial;}
    .main-navigation-content > ul > li {position: relative;}
    .main-navigation-content > ul > li a {/* padding-left: 1rem; *//* padding-right: 1rem; */}
    .main-navigation-content > ul li > ul li {position:relative;background: #fff;}
    .main-navigation-content > ul li ul {transition:all 0.35s ease;}
    }
    <div class="main-navigation">
        <div class="main-navigation-trigger">
            <div class="main-navigation-trigger-inner"></div>
        </div>
        <div class="main-navigation-content">
            <ul>
                <li><a href="">Home</a></li>
                <li><a href="">Features</a>
                    <ul>
                        <li><a href="">Graphic Designing</a></li>
                        <li><a href="">Web Designing</a></li>
                        <li><a href="">App Development</a>
                            <ul>
                                <li><a href="">Android</a></li>
                                <li><a href="">IOS</a></li>
                                <li><a href="">Hybrid</a></li>
                            </ul>
                        </li>
                    </ul>
                </li>
                <li><a href="">About Us</a>
                    <ul>
                        <li><a href="">History and Foundation</a></li>
                        <li><a href="">Company Overview</a></li>
                        <li><a href="">Working Strategy</a>
                            <ul>
                                <li><a href="">Employer Profile</a></li>
                                <li><a href="">Manpower Allocation</a></li>
                                <li><a href="">Marketing Strategy</a></li>
                            </ul>
                        </li>
                    </ul>
                </li>
                <li><a href="">Services</a>
                    <ul>
                        <li><a href="">Graphic Designing</a></li>
                        <li><a href="">Web Designing</a></li>
                        <li><a href="">App Development</a>
                            <ul>
                                <li><a href="">Android</a></li>
                                <li><a href="">IOS</a></li>
                                <li><a href="">Hybrid</a></li>
                            </ul>
                        </li>
                    </ul>
                </li>
                
                <li><a href="">Contact</a></li>
            </ul>
        </div>
    </div>
        3
  •  0
  •   bitski    3 年前

    您需要在菜单上循环两次才能:

    1. 设置点击事件监听器(你这样做了,看起来不错 submenuToggle() ),但是click事件侦听器的回调需要修复。它需要单击的链接的父级 <li> 元素设置为参数。

    论点

    <li>  // submenu's parent list element, set as argument
      <a href="">Working Strategy</a> // link with set click 
      <ul> // submenu to display or hide
    

    已编辑的事件侦听器:

    item.addEventListener("click",(e)=>{
      e.preventDefault();
      let linkParentListElement = e.parentElement; 
      slideToggle(linkParentListElement);
    });
    
    1. 根据其同级菜单是否设置每个子菜单的显示属性 <a> 是否被点击。我会做2。在专用方法中循环,例如您的初始slideToggle(linkParentListElement)。让它在上面循环 <李> 具有方法的子菜单 forEach() . 在该方法的回调中,将每个项目(如果是)与单击的链接(aka)(如果是 linkParentListElement :

    if(true):设置其直接子项的样式 <ul> display: block; ,

    if(false):设置其直接子项的样式 <ul> display: none; .

    /* toggle click function for submenus */
    slideToggle=(el)=> {
      let submenuParentListElement = el,
      listItems = document.querySelectorAll('div > ul > li > ul > li');
      
      listItems.forEach(function(el) {
        if (el === linkParentListElement) {
          // set display: block to its direct child <ul>
        } else {
      // set display: block to its direct child <ul>
        }
      });
    }