Vanilla Javascript 切换下拉菜单

Ser*_*gio 3 javascript navigation toggle

我的大脑已经检查了周末......

我正在寻找一种纯 Javascript 解决方案,如果在单击另一个主菜单项时打开一个下拉菜单框,则先前打开的下拉列表将关闭,然后显示新单击的主菜单项的下拉列表。我知道这可能很简单,但我想不出一个不复杂的解决方案。

此外,如果您单击菜单项外部(文档中非菜单项或下拉框的任何位置)应关闭所有打开的下拉列表。

感谢您的任何帮助。

function testFunc(el) {
  var parent = el.parentElement;
  var dd = parent.lastChild.previousSibling;
  dd.classList.toggle('show');
}
Run Code Online (Sandbox Code Playgroud)
ul { list-style: none; margin: 0; padding: 0; }
ul li {
  width: 100px;
  float: left;
  background: #dbdbdb;
  line-height: 2em;
  text-align: center;
  margin: 0 5px;
  cursor: pointer;
}
ul li span {
  display: block;
}
ul li ul {
  display: none;
}

.show {
  display: block;
}
Run Code Online (Sandbox Code Playgroud)
<ul>
  <li>
    <span onclick="testFunc(this)">Item 1</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 2</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 3</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 4</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
</ul>
Run Code Online (Sandbox Code Playgroud)

Iva*_*van 6

切换菜单可见性

您可以将上次打开的菜单保存opened在函数外部的变量中。然后当菜单被点击时,如果opened不是null,它将切换opened(即隐藏上次打开的菜单)并切换点击的项目。

let opened = null

function testFunc(el) {

  // gets the <ul> element of the clicked menu item
  const menu = el.parentElement.lastChild.previousSibling;

  if (!opened) {

    // no menu item is shown
    opened = menu
    opened.classList.toggle('show');

  } else if (menu == opened) {

    // the clicked item is already showing
    menu.classList.toggle('show')
    opened = null

  } else {

    // the clicked item is hiddden but another one is showing
    opened.classList.toggle('show')
    opened = menu
    opened.classList.toggle('show')

  }

}
Run Code Online (Sandbox Code Playgroud)

这是代码:

let opened = null

function testFunc(el) {

  // gets the <ul> element of the clicked menu item
  const menu = el.parentElement.lastChild.previousSibling;

  if (!opened) {

    // no menu item is shown
    opened = menu
    opened.classList.toggle('show');

  } else if (menu == opened) {

    // the clicked item is already showing
    menu.classList.toggle('show')
    opened = null

  } else {

    // the clicked item is hiddden but another one is showing
    opened.classList.toggle('show')
    opened = menu
    opened.classList.toggle('show')

  }

}
Run Code Online (Sandbox Code Playgroud)
let opened = null

function testFunc(el) {

  const menu =  el.parentElement.lastChild.previousSibling;
  
  if(!opened) {
    opened = menu
    opened.classList.toggle('show');
  } else if(menu == opened) {
    menu.classList.toggle('show')
    opened = null
  } else {
    opened.classList.toggle('show')
    opened = menu
    opened.classList.toggle('show')
  }
  
}
Run Code Online (Sandbox Code Playgroud)
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul li {
  width: 100px;
  float: left;
  background: #dbdbdb;
  line-height: 2em;
  text-align: center;
  margin: 0 5px;
  cursor: pointer;
}

ul li span {
  display: block;
}

ul li ul {
  display: none;
}

.show {
  display: block;
}
Run Code Online (Sandbox Code Playgroud)


具有 ES6 语法的变体

这是一个带有一些 ES6 语法的变体,注意我已经更改了 HTML 命名结构以更好地维护代码,通过类名调用元素允许

  • 不必使用内联事件侦听器

  • 在一行中调用所有菜单项

这是 JavaScript 代码:

let opened = null
const toggleVisibility = e => e.classList.toggle('show')

const toggleDropDown = e => {

  const clickedItem = e.target.parentElement.lastChild.previousSibling

  toggleVisibility(clickedItem);

  if (!opened) {
    opened = clickedItem
  } else if (opened == clickedItem) {
    opened = null
  } else {
    toggleVisibility(opened);
    opened = clickedItem
  }

}

[...document.querySelectorAll('.dropDown')].forEach(dropDown => dropDown.addEventListener('click', toggleDropDown))
Run Code Online (Sandbox Code Playgroud)

<ul>
  <li>
    <span onclick="testFunc(this)">Item 1</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 2</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 3</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span onclick="testFunc(this)">Item 4</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
</ul>
Run Code Online (Sandbox Code Playgroud)
let opened = null
const toggleVisibility = e => e.classList.toggle('show')

const toggleDropDown = e => {

  const clickedItem = e.target.parentElement.lastChild.previousSibling

  toggleVisibility(clickedItem);

  if (!opened) {
    opened = clickedItem
  } else if (opened == clickedItem) {
    opened = null
  } else {
    toggleVisibility(opened);
    opened = clickedItem
  }

}

[...document.querySelectorAll('.dropDown')].forEach(dropDown => dropDown.addEventListener('click', toggleDropDown))
Run Code Online (Sandbox Code Playgroud)
let opened = null
const toggleVisibility = e => e.classList.toggle('show')

const toggleDropDown = e => {

  const clickedItem = e.target.parentElement.lastChild.previousSibling

  toggleVisibility(clickedItem);

  if (!opened) {
    opened = clickedItem
  } else if (opened == clickedItem) {
    opened = null
  } else {
    toggleVisibility(opened);
    opened = clickedItem
  }

}

[...document.querySelectorAll('.dropDown')].forEach(dropDown => dropDown.addEventListener('click', toggleDropDown))
Run Code Online (Sandbox Code Playgroud)


单击别处时切换菜单可见性 + 关闭

如果您想在用户单击菜单外时关闭任何打开的菜单,您需要在文档本身上有一个事件侦听器。因此,不是每个菜单按钮都有一个事件侦听器,您将有一个监听文档中发生的任何点击。

事件侦听器将确定单击的项目是否为菜单按钮,在这种情况下,它将运行菜单处理程序。否则它将关闭最后打开的菜单项。

JavaScript 代码:

let opened = null
const toggleVisibility = e => e.classList.toggle('show')

const handleDropdown = e => {

  const clickedItem = e.parentElement.lastChild.previousSibling

  toggleVisibility(clickedItem)

  if (!opened) {
    opened = clickedItem
  } else if (opened == clickedItem) {
    opened = null
  } else {
    toggleVisibility(opened)
    opened = clickedItem
  }

}

const handleClick = e => {

  if (e.target.className.includes('dropDown')) {
    handleDropdown(e.target)
  } else if (opened) {
    toggleVisibility(opened)
    opened = null
  }

}

document.addEventListener('click', handleClick)
Run Code Online (Sandbox Code Playgroud)

这是完整的代码:

ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul li {
  width: 100px;
  float: left;
  background: #dbdbdb;
  line-height: 2em;
  text-align: center;
  margin: 0 5px;
  cursor: pointer;
}

ul li span {
  display: block;
}

ul li ul {
  display: none;
}

.show {
  display: block;
}
Run Code Online (Sandbox Code Playgroud)
<ul>
  <li>
    <span class="dropDown">Item 1</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span class="dropDown">Item 2</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span class="dropDown">Item 3</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
  <li>
    <span class="dropDown">Item 4</span>
    <ul>
      <li>Sub Item 1</li>
      <li>Sub Item 2</li>
    </ul>
  </li>
</ul>
Run Code Online (Sandbox Code Playgroud)
let opened = null
const toggleVisibility = e => e.classList.toggle('show')

const handleDropdown = e => {

  const clickedItem = e.parentElement.lastChild.previousSibling

  toggleVisibility(clickedItem)

  if (!opened) {
    opened = clickedItem
  } else if (opened == clickedItem) {
    opened = null
  } else {
    toggleVisibility(opened)
    opened = clickedItem
  }

}

const handleClick = e => {

  if (e.target.className.includes('dropDown')) {
    handleDropdown(e.target)
  } else if (opened) {
    toggleVisibility(opened)
    opened = null
  }

}

document.addEventListener('click', handleClick)
Run Code Online (Sandbox Code Playgroud)