如何在切换下拉菜单上添加互斥性

How to add mutually exclusivity on toggling dropdown menu

我有一个带有下拉子菜单的菜单。我试图在点击另一个项目时关闭一个项目,这样我就不会同时打开多个项目。上一个问题:有用户建议我看一下互斥函数。我怎样才能将它添加到我的菜单中?

var dropdownBtn = document.querySelectorAll('.menu-btn');

dropdownBtn.forEach(btn => btn.addEventListener('click', function() {
  var menuContent = this.nextElementSibling;
  menuContent.classList.toggle("show");
  
}));
.menu-btn {
  background: #e0e0e0;
  padding: 10px;
  margin: 5px 0px 0px 0px;
}

.menu-btn:hover {
  background: #000;
  color: #fff;
}


.drop_container {
   display: none;
   background-color: #017575;
   transition: 0.3s;
   opacity: 0;
}

.drop_container.show {
  display: contents;
  visibility: visible;
  opacity: 1;
}

.drop_container > .item {
  display: flex;
  flex-direction: column;
  margin-left: 10px;
  padding: 10px 0px 0px 0px;
}
<div class="dropdown-menu">

<div class="menu-btn">One</div>
<div class="drop_container">
  <a class="item" href="#">Contact Us</a>
  <a class="item" href="#">Visit Us</a>
</div>

<div class="menu-btn">Two</div>
<div class="drop_container">
  <a class="item" href="#">Contact Us</a>
  <a class="item" href="#">Visit Us</a>
</div>

</div>

将之前单击的菜单存储在一个变量中,如果单击了另一个菜单,则将其清除 class

var dropdownBtn = document.querySelectorAll('.menu-btn'),
    lastOpened = null;

dropdownBtn.forEach(btn => btn.addEventListener('click', function() {
    var menuContent = this.nextElementSibling;
    menuContent.classList.toggle("show");

    if (lastOpened && lastOpened !== menuContent)
      lastOpened.classList.remove("show");

    lastOpened = menuContent;
  
}));
.menu-btn {
  background: #e0e0e0;
  padding: 10px;
  margin: 5px 0px 0px 0px;
}

.menu-btn:hover {
  background: #000;
  color: #fff;
}


.drop_container {
   display: none;
   background-color: #017575;
   transition: 0.3s;
   opacity: 0;
}

.drop_container.show {
  display: contents;
  visibility: visible;
  opacity: 1;
}

.drop_container > .item {
  display: flex;
  flex-direction: column;
  margin-left: 10px;
  padding: 10px 0px 0px 0px;
}
<div class="dropdown-menu">

<div class="menu-btn">One</div>
<div class="drop_container">
  <a class="item" href="#">Contact Us</a>
  <a class="item" href="#">Visit Us</a>
</div>

<div class="menu-btn">Two</div>
<div class="drop_container">
  <a class="item" href="#">Contact Us</a>
  <a class="item" href="#">Visit Us</a>
</div>

</div>

您可以添加一个功能来关闭除您传入的菜单之外的所有菜单:

var dropdownBtn = document.querySelectorAll('.menu-btn');

dropdownBtn.forEach(btn => btn.addEventListener('click', function() {
  var menuContent = this.nextElementSibling;
  closeMenusExcept(menuContent);
  menuContent.classList.toggle("show");
}));

function closeMenusExcept(menuContent) {
  dropdownBtn.forEach((element) => {
    if (menuContent !== element.nextElementSibling) {
      element.nextElementSibling.classList.remove("show");
    }
  })
}
.menu-btn {
  background: #e0e0e0;
  padding: 10px;
  margin: 5px 0px 0px 0px;
}

.menu-btn:hover {
  background: #000;
  color: #fff;
}

.drop_container {
  display: none;
  background-color: #017575;
  transition: 0.3s;
  opacity: 0;
}

.drop_container.show {
  display: contents;
  visibility: visible;
  opacity: 1;
}

.drop_container>.item {
  display: flex;
  flex-direction: column;
  margin-left: 10px;
  padding: 10px 0px 0px 0px;
}
<div class="dropdown-menu">

  <div class="menu-btn">One</div>
  <div class="drop_container">
    <a class="item" href="#">Contact Us</a>
    <a class="item" href="#">Visit Us</a>
  </div>

  <div class="menu-btn">Two</div>
  <div class="drop_container">
    <a class="item" href="#">Contact Us</a>
    <a class="item" href="#">Visit Us</a>
  </div>

</div>

备注 我更改了 class 名称,以便于输入和阅读。

我们将使用编程范式调用事件委托。 将事件绑定到祖先标签(一个包含您要控制的所有标签的标签)

图一

const menu = document.querySelector(".dropdown"); 
menu.addEventListener('click', //...

接下来,设计一个事件处理程序,仅在单击正确的标签时做出反应

图二

//...
  function(event) {
   // This is the tag the user clicked 
   const clicked = event.target;
   // Find .show 
   const current = document.querySelector('.show');
   // See if the clicked tag has .show class
   let state = clicked.matches('.show');
   // Only react if the clicked tag has .btn class
   if (clicked.matches('.btn')) {//...

注意: .show class 现在分配给 .btn。见图四

图三

//...
      // if there already is a .show    
      if (current) {
        // remove .show
        current.classList.remove('show');
      }
      // if the clicked tag did not have .show previously...
      if (!state) {
        // ...add .show to it
        clicked.classList.add("show");
      }
    }
  });

在 CSS 中,此规则集使用等效于 .nextElementSibling

的相邻兄弟组合器

图四

/* .btn.show + .list <=that's the next sibling */
.show+.list {
  display: block;
} 

删除了 visibilityopacity,因为原始状态是 display:none,这是一个禁止任何类型的 transition 的开关(也已删除)。 display: content 已更改为 display: blockdisplay: content 没有任何标准行为,应用时,.item 是黑白的,但一旦替换它们原来的绿色就会返回。作为处理 CSS 的一般规则,如果您没有在示例中看到它被使用,请不要使用它,因为可能有充分的理由不使用它。

使用此设置,只要它在祖先标记内,您就不必担心有多少 button/items。此外,如果您动态添加任何 button/items,则它们不需要绑定到事件。您所需要的只是为您要侦听的每个事件设置一个事件侦听器。

const menu = document.querySelector(".dropdown");

const btns = document.querySelectorAll('.btn');

menu.addEventListener('click', function(event) {
  const clicked = event.target;
  const current = document.querySelector('.show');
  let state = clicked.matches('.show');
  if (clicked.matches('.btn')) {
    if (current) {
      current.classList.remove('show');
    }
    if (!state) {
      clicked.classList.add("show");
    }
  }
});
.btn {
  background: #e0e0e0;
  padding: 10px;
  margin: 5px 0px 0px 0px;
}

.btn:hover {
  background: #000;
  color: #fff;
}

.list {
  display: none;
  background-color: #017575;
}

.show+.list {
  display: block;
}

.list>.item {
  display: flex;
  flex-direction: column;
  margin-left: 10px;
  padding: 10px 0px 0px 0px;
}
<div class="dropdown">

  <div class="btn">One</div>
  <div class="list">
    <a class="item" href="#">Contact Us</a>
    <a class="item" href="#">Visit Us</a>
  </div>

  <div class="btn">Two</div>
  <div class="list">
    <a class="item" href="#">Contact Us</a>
    <a class="item" href="#">Visit Us</a>
  </div>

  <div class="btn">Three</div>
  <div class="list">
    <a class="item" href="#">Contact Us</a>
    <a class="item" href="#">Visit Us</a>
  </div>

</div>