我有两个带有子菜单的项目的导航。我目前有一个可以打开和关闭的类,单击时会显示这些子菜单。
我想要它,所以当我单击页面上的任何位置时,如果它们可见,它们就会消失。
目前我认为我的代码对于它目前实现的功能来说有点冗长,也许使用 e.target
会更好。点击时?
您当前可以通过单击任一菜单项来关闭和打开菜单(这包括再次单击可见菜单项)。
我想通过单击菜单项外部来删除“可见”类,我可以做一个简单的 document.addEventListener('click', function(e) {})
如果它正在显示,则在整个文档上删除“可见”类,但这似乎不起作用。
注:我需要在不使用 blur
的情况下执行此操作事件监听器
密码笔:https://codepen.io/emilychews/pen/bGWVVpq
var menu_item_1 = document.getElementById('item-1'),
menu_item_2 = document.getElementById('item-2'),
sub_menu_item_1 = document.getElementById('sub-item-1'),
sub_menu_item_2 = document.getElementById('sub-item-2')
if (menu_item_1) {
menu_item_1.addEventListener('click', function(e){
sub_menu_item_1.classList.toggle('visible')
// hide submenu 2
sub_menu_item_2.classList.remove('visible')
}, false)
}
if (menu_item_2) {
menu_item_2.addEventListener('click', function(e){
sub_menu_item_2.classList.toggle('visible')
// hide submenu 1
sub_menu_item_1.classList.remove('visible')
}, false)
}
body {
display: flex;
justify-content: center;
margin: 0;
height: 100vh;
width: 100%;
}
header {
margin-top: 2rem;
display: flex;
width: 50%;
justify-content: space-evenly;
align-items: center;
padding: 1rem;
background: red;
height: 2rem;
}
.menu-item {
position: relative;
padding: 1rem;
background: yellow;
cursor: pointer;
}
.submenu {
display: none; /* changes to 'block' with javascript */
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
.submenu.visible {
display:block;
}
<header>
<div id="item-1" class="menu-item menu-item-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1">SUB-ITEM-1</div>
</div>
<div id="item-2" class="menu-item menu-item-2">ITEM 2
<div id="sub-item-2" class="submenu submenu-2">SUB-ITEM-2</div>
</div>
</header>
最佳答案
有几种不同的方法可以实现这一点,并非所有方法都涉及 JS,我将在下面概述一些可能的方法:
纯 CSS:
第一个(也是最简单的)是仅使用 css。这再次使用 tabindex="-1"
喜欢 Samuel's answer使您的菜单项按钮具有焦点。一旦按钮获得焦点,您可以使用 :focus
将一些 CSS 应用到焦点项的关联子菜单。伪类选择器:
.menu-item:focus > .submenu { /* select the focused menu-item's child elements with the class submenu */
display: block;
}
请参见下面的示例:body {
display: flex;
justify-content: center;
margin: 0;
height: 100vh;
width: 100%;
}
header {
margin-top: 2rem;
display: flex;
width: 50%;
justify-content: space-evenly;
align-items: center;
padding: 1rem;
background: red;
height: 2rem;
}
.menu-item {
position: relative;
padding: 1rem;
background: yellow;
cursor: pointer;
}
.submenu {
display: none; /* changes to 'block' with CSS */
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
.menu-item:focus > .submenu {
display: block;
}
<header>
<div id="item-1" class="menu-item menu-item-1" tabindex="-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1">SUB-ITEM-1</div>
</div>
<div id="item-2" class="menu-item menu-item-2" tabindex="-1">ITEM 2
<div id="sub-item-2" class="submenu submenu-2">SUB-ITEM-2</div>
</div>
</header>
这样做的主要缺点是我们使用
:focus
,这意味着如果您再次单击菜单项,它将保持焦点而不是模糊,结果将使菜单项保持在 View 中而不是隐藏它。以下使用 JS 的方法可以处理这种情况:向文档添加事件监听器:
另一种可能的解决方案是更新您的 JS。这包括使用
querySelectorAll()
选择所有菜单项和子菜单项。 .然后,您可以通过浏览 NodeList 将事件监听器添加到您的菜单项。调用 .querySelectorAll()
返回.当您单击一个菜单项时,您可以使用 .querySelector()
获取其关联的子菜单项。当前menuItem
.为了在您单击屏幕上的其他位置时隐藏项目,您可以通过向其添加事件监听器来监听文档上的单击事件,并相应地隐藏您的子菜单项。在添加到菜单项的事件监听器中,您可以调用 .stopPropagation()
防止菜单项上的单击事件冒泡到文档并导致文档事件监听器执行(并隐藏所有项目)。const menuItems = document.querySelectorAll(".menu-item"); // Get all menu items in an array-like structure (NodeList)
const submenuItems = document.querySelectorAll(".submenu"); // select all submenu items
const hideMenus = (menus, ignore) => menus.forEach(menu => { // loop through all items (use: [...menus].forEach((menu) => {) for better browser support)
if (menu !== ignore) // if we encounter an element that we want to keep visible, skip it, otherwise, remove its visibility
menu.classList.remove("visible");
});
menuItems.forEach(menuItem => { // loop through the NodeList menu items
menuItem.addEventListener("click", (e) => {
e.stopPropagation(); // stop event from bubbling up to the document and executing the below `document.addEventListener()` when menu item is clicked
if (e.target === menuItem) { // don't hide when we click on a sub-menu-item (e.target = child sub-menu-item if that is clicked)
const thisSubmenu = menuItem.querySelector(".submenu");
thisSubmenu.classList.toggle('visible'); // toggle visibility of submenu under our item
hideMenus(submenuItems, thisSubmenu); // hide all other submenus
}
});
});
document.addEventListener("click", (e) => {
hideMenus(submenuItems);
});
body {
display: flex;
justify-content: center;
margin: 0;
height: 100vh;
width: 100%;
}
header {
margin-top: 2rem;
display: flex;
width: 50%;
justify-content: space-evenly;
align-items: center;
padding: 1rem;
background: red;
height: 2rem;
}
.menu-item {
position: relative;
padding: 1rem;
background: yellow;
cursor: pointer;
}
.submenu {
display: none;
/* changes to 'block' with javascript */
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
.submenu.visible {
display: block;
}
<header>
<div id="item-1" class="menu-item menu-item-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1">SUB-ITEM-1</div>
</div>
<div id="item-2" class="menu-item menu-item-2">ITEM 2
<div id="sub-item-2" class="submenu submenu-2">SUB-ITEM-2</div>
</div>
</header>
使用事件委托(delegate):
您可以更新上面的示例以使用 event delegation ,它允许您仅在文档上使用一个事件监听器,而不是为每个菜单项添加一个(从而有助于限制浏览器使用的资源)。然后您可以使用
e.target
和 .closest()
确定您单击了哪个元素(有关详细信息,请参阅代码注释):const submenuItems = document.querySelectorAll(".submenu"); // select all submenu items
const hideMenus = (menus, ignore) => menus.forEach(menu => { // loop through all items (use: [...menus].forEach((menu) => {) for better browser support)
if(menu !== ignore) // if we encounter an element that we want to keep visible, skip it, otherwise, remove its visibility
menu.classList.remove("visible");
});
document.addEventListener("click", (e) => {
const clickedItem = e.target, menuItem = clickedItem.closest(".menu-item");
// v-- use `= menuItem && menuItem.querySelector(...)` for better browser support
const thisSubmenu = menuItem?.querySelector(".submenu"); // grab the submenu from the menuItem we clicked on (or parent menuItem if we clicked on a submenu item)
if(clickedItem === menuItem) // we clicked on a menu-item
thisSubmenu.classList.toggle('visible'); // toggle visibility of submenu under our menu-item
hideMenus(submenuItems, thisSubmenu);
});
body {
display: flex;
justify-content: center;
margin: 0;
height: 100vh;
width: 100%;
}
header {
margin-top: 2rem;
display: flex;
width: 50%;
justify-content: space-evenly;
align-items: center;
padding: 1rem;
background: red;
height: 2rem;
}
.menu-item {
position: relative;
padding: 1rem;
background: yellow;
cursor: pointer;
}
.submenu {
display: none; /* changes to 'block' with javascript */
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
.submenu.visible {
display:block;
}
<header>
<div id="item-1" class="menu-item menu-item-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1">SUB-ITEM-1</div>
</div>
<div id="item-2" class="menu-item menu-item-2">ITEM 2
<div id="sub-item-2" class="submenu submenu-2">SUB-ITEM-2</div>
</div>
</header>
关于javascript - 在元素外部单击时切换元素并移除其可见性 - JavaScript,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68233038/