我的大脑已经结束了周末......
我正在寻找一个简单的Javascript解决方案,如果在单击另一个主菜单项时打开一个下拉菜单框,则先前打开的下拉菜单将关闭,然后显示新单击的主菜单项的下拉列表。我知道这可能很简单,但我无法想出一个没有问题的解决方案。
此外,如果您单击菜单项外部(文档中不是菜单项或下拉框的任何位置),则应关闭所有打开的下拉列表。
感谢您的帮助。
function testFunc(el) {
var parent = el.parentElement;
var dd = parent.lastChild.previousSibling;
dd.classList.toggle('show');
}
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;
}
<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>
答案 0 :(得分:4)
您可以将最后打开的菜单保存在函数外的变量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')
}
}
以下是代码:
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')
}
}
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;
}
<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>
这是一个带有一些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))
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))
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;
}
<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>
如果要在用户点击菜单外部时关闭任何已打开的菜单,则需要在文档本身上拥有事件监听器。因此,不是每个菜单按钮都有一个事件监听器,您将只有一个监视文档中发生的任何点击。
事件监听器将确定所单击的项是否是菜单按钮,在这种情况下,它将运行菜单处理程序。否则它将关闭最后打开的菜单项。
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)
以下是完整代码:
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)
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;
}
<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>
答案 1 :(得分:0)
很难与Ivan的答案抗衡,但这将是我使用ES6语法解决同一问题的解决方案:
class Dropdown {
constructor() {
this.listen();
}
// Listen to ALL (!) click events to also catch clicks OUTSIDE the dropdowns
listen() {
document.addEventListener('click', (e) => {
if (e.target.classList.contains('dropdown')) {
this.closeOthers(e.target);
this.handleClick(e.target);
} else {
this.closeOthers(null);
}
});
}
// Add or remove 'expanded' CSS class, depending on the current situation
handleClick(dropdown) {
if (dropdown.classList.contains('expanded')) {
dropdown.classList.remove('expanded');
} else {
dropdown.classList.add('expanded');
}
}
// Close all dropdowns except the one that gets passed as the element parameter
// Note that we may also pass null in order to close ALL dropdowns
closeOthers(element) {
document.querySelectorAll('.dropdown').forEach((dropdown) => {
if (element != dropdown) {
dropdown.classList.remove('expanded');
}
});
}
}
document.addEventListener('DOMContentLoaded', () => new Dropdown);
对我有用。不知道它是否可以为其他人工作。反馈表示赞赏。