切换元素并在单击元素外部时移除其可见性 - JavaScript
Toggle An Element And Also Remove Its Visibility When Clicking Outside Of The Element - JavaScript
我有一个包含两个子菜单项的导航。我目前有一个 class 可以打开和关闭,单击时会显示这些子菜单。
我希望当我点击页面上的任何地方时,如果它们可见,它们就会消失。
目前我认为我的代码对于目前实现的功能来说有点冗长,也许在单击时使用 e.target
会更好?
您目前可以通过单击任一菜单项(包括再次单击可见菜单项)来关闭和打开菜单。
我想通过单击菜单项外部来删除 'visible' class 我可以对整个文档做一个简单的 document.addEventListener('click', function(e) {})
来删除 'visible' class 如果它正在显示,但这似乎不起作用。
注意:我需要在不使用 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>
解决此问题的一种方法是利用 focus
和 blur
事件。 div
元素默认不接收焦点,但我们可以添加 tabindex
属性来解决这个问题。
当您单击 div 时,它会获得焦点,因此我们只需监听 blur
事件并隐藏 div。
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)
}
// listen for blur events
menu_item_1.addEventListener('blur', function(e){ sub_menu_item_1.classList.remove('visible')
})
menu_item_2.addEventListener('blur', function(e){ sub_menu_item_2.classList.remove('visible')
})
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" tabindex="-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1" tabindex="-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" tabindex="-1">SUB-ITEM-2</div>
</div>
</header>
有几种不同的方法可以实现这一点,并非所有方法都涉及 JS,我将在下面概述几种可能的方法:
纯CSS:
第一个(可能也是最简单的)是仅使用 css。这再次使用 tabindex="-1"
like to make your menu item buttons focusable. Once a button is focused, you can apply some CSS to the focused item's associated submenu using the :focus
pseudo-class 选择器:
.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
,这意味着如果您再次单击某个菜单项,它将保持聚焦而不是模糊,结果将保留菜单项目在视图中而不是隐藏它。不过,以下使用 JS 的方法可以处理这种情况:
正在向文档添加事件侦听器:
另一种可能的解决方案是更新您的 JS。这涉及使用 querySelectorAll()
. You can then add event listeners to your menu-items by looing through the NodeList returned by the call to .querySelectorAll()
. When you click on a menu-item, you can grab its associated submenu item using .querySelector()
on the current menuItem
. In order to hide the items when you click elsewhere on the screen, you can listen for click events on the document by adding an event listener to that, and hide your submenu items accordingly. Within your event listeners that you add to your menu items, you can call .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>
使用事件委托:
您可以更新上面的示例以使用 event delegation, which allows you to only use one event listener on the document rather than adding one per menu item (thus helping limit the resources used by your browser). You can then use e.target
and .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>
我还在每个子菜单上添加了 console.log
以确保它们在菜单关闭前是交互式的。
const menus = Array.from(document.querySelectorAll('.menu-item'));
function handleOnClickOutsideMenu(e) {
const target = menus.filter(menu => menu.contains(e.target));
if (target.length) {
// user is clicking inside a menu: don't do anything.
// this is handled by handleOnMenuToggle.
return;
}
// close all the menus in the page
menus.forEach(menu => menu.classList.remove('expanded'));
// we don't need it anymore (it is added dynamically in the handleOnMenuToggle)
document.removeEventListener('click', handleOnClickOutsideMenu);
}
function handleOnMenuToggle(e) {
// close other menus
menus
.filter(menu => menu !== e.currentTarget)
.forEach(menu => menu.classList.remove('expanded'));
// toggle current menu
e.currentTarget.classList.toggle('expanded');
// Important optimization:
// we want the click event on the document only when a menu is expanded
if (e.currentTarget.classList.contains('expanded')) {
document.addEventListener('click', handleOnClickOutsideMenu);
} else {
document.removeEventListener('click', handleOnClickOutsideMenu);
}
}
menus.forEach(menu => {
menu.addEventListener('click', handleOnMenuToggle);
});
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;
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
/* adding .expanded on menu-item so it can handle multiple sub menu */
.menu-item.expanded .submenu {
display: block;
}
<header>
<div id="item-1" class="menu-item menu-item-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1" onclick="console.log(this)">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" onclick="console.log(this)">SUB-ITEM-2</div>
</div>
</header>
我有一个包含两个子菜单项的导航。我目前有一个 class 可以打开和关闭,单击时会显示这些子菜单。
我希望当我点击页面上的任何地方时,如果它们可见,它们就会消失。
目前我认为我的代码对于目前实现的功能来说有点冗长,也许在单击时使用 e.target
会更好?
您目前可以通过单击任一菜单项(包括再次单击可见菜单项)来关闭和打开菜单。
我想通过单击菜单项外部来删除 'visible' class 我可以对整个文档做一个简单的 document.addEventListener('click', function(e) {})
来删除 'visible' class 如果它正在显示,但这似乎不起作用。
注意:我需要在不使用 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>
解决此问题的一种方法是利用 focus
和 blur
事件。 div
元素默认不接收焦点,但我们可以添加 tabindex
属性来解决这个问题。
当您单击 div 时,它会获得焦点,因此我们只需监听 blur
事件并隐藏 div。
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)
}
// listen for blur events
menu_item_1.addEventListener('blur', function(e){ sub_menu_item_1.classList.remove('visible')
})
menu_item_2.addEventListener('blur', function(e){ sub_menu_item_2.classList.remove('visible')
})
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" tabindex="-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1" tabindex="-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" tabindex="-1">SUB-ITEM-2</div>
</div>
</header>
有几种不同的方法可以实现这一点,并非所有方法都涉及 JS,我将在下面概述几种可能的方法:
纯CSS:
第一个(可能也是最简单的)是仅使用 css。这再次使用 tabindex="-1"
like :focus
pseudo-class 选择器:
.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
,这意味着如果您再次单击某个菜单项,它将保持聚焦而不是模糊,结果将保留菜单项目在视图中而不是隐藏它。不过,以下使用 JS 的方法可以处理这种情况:
正在向文档添加事件侦听器:
另一种可能的解决方案是更新您的 JS。这涉及使用 querySelectorAll()
. You can then add event listeners to your menu-items by looing through the NodeList returned by the call to .querySelectorAll()
. When you click on a menu-item, you can grab its associated submenu item using .querySelector()
on the current menuItem
. In order to hide the items when you click elsewhere on the screen, you can listen for click events on the document by adding an event listener to that, and hide your submenu items accordingly. Within your event listeners that you add to your menu items, you can call .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>
使用事件委托:
您可以更新上面的示例以使用 event delegation, which allows you to only use one event listener on the document rather than adding one per menu item (thus helping limit the resources used by your browser). You can then use e.target
and .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>
我还在每个子菜单上添加了 console.log
以确保它们在菜单关闭前是交互式的。
const menus = Array.from(document.querySelectorAll('.menu-item'));
function handleOnClickOutsideMenu(e) {
const target = menus.filter(menu => menu.contains(e.target));
if (target.length) {
// user is clicking inside a menu: don't do anything.
// this is handled by handleOnMenuToggle.
return;
}
// close all the menus in the page
menus.forEach(menu => menu.classList.remove('expanded'));
// we don't need it anymore (it is added dynamically in the handleOnMenuToggle)
document.removeEventListener('click', handleOnClickOutsideMenu);
}
function handleOnMenuToggle(e) {
// close other menus
menus
.filter(menu => menu !== e.currentTarget)
.forEach(menu => menu.classList.remove('expanded'));
// toggle current menu
e.currentTarget.classList.toggle('expanded');
// Important optimization:
// we want the click event on the document only when a menu is expanded
if (e.currentTarget.classList.contains('expanded')) {
document.addEventListener('click', handleOnClickOutsideMenu);
} else {
document.removeEventListener('click', handleOnClickOutsideMenu);
}
}
menus.forEach(menu => {
menu.addEventListener('click', handleOnMenuToggle);
});
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;
padding: 1rem;
background: lightblue;
position: absolute;
top: 4rem;
left: 0;
width: 6rem;
}
/* adding .expanded on menu-item so it can handle multiple sub menu */
.menu-item.expanded .submenu {
display: block;
}
<header>
<div id="item-1" class="menu-item menu-item-1">ITEM 1
<div id="sub-item-1" class="submenu submenu-1" onclick="console.log(this)">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" onclick="console.log(this)">SUB-ITEM-2</div>
</div>
</header>