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

How can i collapse Vanilla JS Multi level Menu when other menu is Opened

我使用纯 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>

您需要循环菜单 2 次才能:

  1. set click-eventListeners(你用 submenuToggle() 似乎没问题),但点击事件侦听器的回调需要修复。它需要将单击的 link 的 parent <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> 是否被点击来设置每个子菜单 display-property。我会在专用方法中执行 2. 循环,例如您的初始 slideToggle(linkParentListElement)。使用方法 forEach() 使其在子菜单 children 上循环 <li>。 在该方法的回调中,比较每个项目是否与点击的 link inside aka 如果它是 linkParentListElement:

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

if (false): 将其直接child<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>
    }
  });
}

我试图理解您的代码以改进它,但几个小时后放弃了这个想法。但是,为了不放弃自己,您可以使用以下解决方法...
我希望至少我的代码能在将来帮助您简化您的代码。

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    : '6F'; /*'05'; */
  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 '6F' is not symetric ! */
  }
.main-nav li.submenu.show > a::after {
  transform  : rotate(-90deg);
  }
<div class="main-nav"></div>

你好朋友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>