我们如何通过外部点击关闭切换菜单
How can we close a toggle menu with an outside click
我是编码新手,我制作了一个外观不错的网站 (https://garibpathshala.in/),带有用于移动设备的切换导航菜单。
有什么方法可以让我们在菜单外单击时关闭菜单。
请看看我的代码并帮助我:)
HTML
<ul class="header-nav-links">
<li class="active"><a href="https://garibpathshala.in">HOME</a></li>
<li><a href="#projects_section">PROJECTS</a></li>
<li><a href="#meet_the_team_section">TEAM</a></li>
<li><a href="#about_us_section">ABOUT</a></li>
<li><a href="https://gallery.garibpathshala.in">GALLERY</a></li>
<li><a href="https://contact.garibpathshala.in">CONTACT</a></li>
<li><a href="https://donate.garibpathshala.in">DONATE</a></li>
<li><a href="https://join.garibpathshala.in">JOIN US</a></li>
</ul>
<div class="burger">
<div line1></div>
<div line2></div>
<div line3></div>
</div>
JS
const burger = document.querySelector(".burger");
const navLinks = document.querySelector(".header-nav-links");
const links = document.querySelectorAll(".header-nav-links li");
//Toggle Nav
burger.addEventListener("click", () => {
navLinks.classList.toggle("open");
//Animate Links
links.forEach((link, index) => {
if (link.style.animation) {
link.style.animation = ""
}else{
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7+0.2}s`;
}
});
});
Here is a screenshot of the nav menu
您可以在 body
或 document
上添加点击侦听器并依赖 event delegation 采取适当的操作,如以下示例代码所示。
(请参阅 in-code 评论以获得进一步说明。)
// Selects some DOM elements and converts the collection to an array
const listItems = Array.from(document.querySelectorAll(".header-nav-links > li"));
// Calls `handleMenuDisplay` when anything is clicked
document.addEventListener("click", handleMenuDisplay);
// Defines `handleMenuDisplay`
function handleMenuDisplay(event){ // Listeners can access their triggering events
const clickedThing = event.target; // The event's `target` property is useful
// Depending on what was clicked, takes an appropriate action
if(listItems.includes(clickedThing)){ // Arrays have an `includes` method
openMenu(clickedThing);
}
else{
closeMenu();
}
}
function openMenu(clickedLi){
demo.textContent = clickedLi.dataset.demoText;
}
function closeMenu(){
demo.textContent = "Menu is closed";
}
li{ margin: 7px; padding: 3px; border: 1px solid grey; }
#demo{ margin-left: 2ch; font-size: 1.5em; font-weight: bold; }
<ul class="header-nav-links">
<li data-demo-text="Home menu is open">HOME</li>
<li data-demo-text="Projects menu is open">PROJECTS</li>
<li data-demo-text="Team menu is open">TEAM</li>
<li data-demo-text="About menu is open">ABOUT</li>
</ul>
<p id="demo">Menu is closed</p>
注意:我使用自定义 data-attributes 只是为了让示例代码更简洁一些——它不是事件委托的一部分,每个 li
的显示文本可以很容易地已在脚本中手动写出。
基于其他解决方案:
一种更简单的方法是使用 DOM 元素的焦点和模糊状态来处理菜单的状态。
document.querySelectorAll('.menu').forEach((menu) => {
const items = menu.querySelector('.menu-items');
menu.addEventListener('click', (e) => {
items.classList.remove("hide");
menu.focus(); // Probably redundant but just in case!
});
menu.addEventListener('blur', () => {
items.classList.add("hide");
});
});
.menu {
cursor: pointer;
display: inline-block;
}
.menu > span {
user-select: none;
}
.menu:focus {
outline: none;
border: none;
}
.hide {
display: none;
}
.menu-items > * {
user-select: none;
}
<div class="menu" tabindex="0">
<span>Menu +</span>
<div class="menu-items hide">
<div>Item 0</div>
<div>Item 1</div>
<div>Item 2</div>
</div>
</div>
这里的秘诀是给 .menu
div 一个 tabindex
以便它可以被浏览器聚焦。
JS 的首要任务:
- 在页面中搜索菜单的任何实例 class
document.querySelectorAll
- 获取当前被
.hide
隐藏的项目元素
- 当有人点击菜单时,从项目中删除
.hide
class
- 这应该聚焦 div,但以防万一
menu.focus
被调用!
- 然后每当有人失去对 div 的关注时,AKA 点击离开等。添加
.hide
class 回来。
这可以扩展为更改按钮的文本,做各种其他事情。它还可以让你的代码超级干净,因为你依赖于浏览器自己的内部状态管理,所以你不必做任何检查。
处理第二次点击关闭
是的,它工作得很好,但是为了让它像大多数人期望的那样运行 UI 我们需要在再次单击菜单 div 时关闭它(替换 span
与任何你需要的 class):
...
menu.querySelector('span').addEventListener('click', (e) => {
e.stopPropagation();
menu.blur();
});
menu.addEventListener('click', (e) => {
items.classList.remove("hide");
menu.focus(); // Probably redundant but just in case!
});
menu.addEventListener('blur', () => {
items.classList.add("hide");
});
...
如果 event.CurrentTarget
不是汉堡菜单和文档中的任何其他内容(html 或 body),您可以从菜单中删除“打开”class被点击。
您还需要对 .hamburger
和 navLinks
本身进行 stopImmediatePropagation
以阻止这些点击被注册为对 body 的点击,因为它们是 [= body 的 33=],否则事件会冒泡到 body。
MDN 参考:https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles
const burger = document.querySelector(".burger");
const navLinks = document.querySelector(".header-nav-links");
const links = document.querySelectorAll(".header-nav-links li");
const body = document.querySelector('html');
//Toggle Nav
burger.addEventListener("click", (e) => {
navLinks.classList.toggle("open");
e.stopImmediatePropagation();
//Animate Links
links.forEach((link, index) => {
if (link.style.animation) {
link.style.animation = "";
}else{
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7+0.2}s`;
}
});
});
navLinks.addEventListener("click", (eve) => {
eve.stopImmediatePropagation();
});
body.addEventListener("click", (ev) => {
if (ev.currentTarget != burger) {
navLinks.classList.remove("open");
}
});
.burger {
display: block;
cursor:pointer;
}
.header-nav-links {
display: block;
}
.header-nav-links.open {
transform: translateX(0%);
}
.header-nav-links {
right: 0;
position: fixed;
height: 92vh;
top: 16vh;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
flex-direction: column;
align-items: center;
width: 50%;
transform: translateX(100%);
transition: transform 0.5s ease-in;
}
.header-nav-links li {
list-style-type: none;
}
.header-nav-links li:hover {
border: 1px solid #fff;
border-radius: 6pc;
background-color: #007bff;
}
.header-nav-links a {
color: whitesmoke;
text-decoration: none;
font-family: Arial, sans-serif;
font-weight: normal;
font-size: 16px;
border: 0px solid white;
transition: 400ms;
padding: 5px 15px;
border-radius: 19px;
}
<ul class="header-nav-links">
<li class="active"><a href="https://garibpathshala.in">HOME</a></li>
<li><a href="#projects_section">PROJECTS</a></li>
<li><a href="#meet_the_team_section">TEAM</a></li>
<li><a href="#about_us_section">ABOUT</a></li>
<li><a href="https://gallery.garibpathshala.in">GALLERY</a></li>
<li><a href="https://contact.garibpathshala.in">CONTACT</a></li>
<li><a href="https://donate.garibpathshala.in">DONATE</a></li>
<li><a href="https://join.garibpathshala.in">JOIN US</a></li>
</ul>
<div class="burger">
BURGER
<div line1></div>
<div line2></div>
<div line3></div>
</div>
我是编码新手,我制作了一个外观不错的网站 (https://garibpathshala.in/),带有用于移动设备的切换导航菜单。
有什么方法可以让我们在菜单外单击时关闭菜单。
请看看我的代码并帮助我:)
HTML
<ul class="header-nav-links">
<li class="active"><a href="https://garibpathshala.in">HOME</a></li>
<li><a href="#projects_section">PROJECTS</a></li>
<li><a href="#meet_the_team_section">TEAM</a></li>
<li><a href="#about_us_section">ABOUT</a></li>
<li><a href="https://gallery.garibpathshala.in">GALLERY</a></li>
<li><a href="https://contact.garibpathshala.in">CONTACT</a></li>
<li><a href="https://donate.garibpathshala.in">DONATE</a></li>
<li><a href="https://join.garibpathshala.in">JOIN US</a></li>
</ul>
<div class="burger">
<div line1></div>
<div line2></div>
<div line3></div>
</div>
JS
const burger = document.querySelector(".burger");
const navLinks = document.querySelector(".header-nav-links");
const links = document.querySelectorAll(".header-nav-links li");
//Toggle Nav
burger.addEventListener("click", () => {
navLinks.classList.toggle("open");
//Animate Links
links.forEach((link, index) => {
if (link.style.animation) {
link.style.animation = ""
}else{
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7+0.2}s`;
}
});
});
Here is a screenshot of the nav menu
您可以在 body
或 document
上添加点击侦听器并依赖 event delegation 采取适当的操作,如以下示例代码所示。
(请参阅 in-code 评论以获得进一步说明。)
// Selects some DOM elements and converts the collection to an array
const listItems = Array.from(document.querySelectorAll(".header-nav-links > li"));
// Calls `handleMenuDisplay` when anything is clicked
document.addEventListener("click", handleMenuDisplay);
// Defines `handleMenuDisplay`
function handleMenuDisplay(event){ // Listeners can access their triggering events
const clickedThing = event.target; // The event's `target` property is useful
// Depending on what was clicked, takes an appropriate action
if(listItems.includes(clickedThing)){ // Arrays have an `includes` method
openMenu(clickedThing);
}
else{
closeMenu();
}
}
function openMenu(clickedLi){
demo.textContent = clickedLi.dataset.demoText;
}
function closeMenu(){
demo.textContent = "Menu is closed";
}
li{ margin: 7px; padding: 3px; border: 1px solid grey; }
#demo{ margin-left: 2ch; font-size: 1.5em; font-weight: bold; }
<ul class="header-nav-links">
<li data-demo-text="Home menu is open">HOME</li>
<li data-demo-text="Projects menu is open">PROJECTS</li>
<li data-demo-text="Team menu is open">TEAM</li>
<li data-demo-text="About menu is open">ABOUT</li>
</ul>
<p id="demo">Menu is closed</p>
注意:我使用自定义 data-attributes 只是为了让示例代码更简洁一些——它不是事件委托的一部分,每个 li
的显示文本可以很容易地已在脚本中手动写出。
基于其他解决方案:
一种更简单的方法是使用 DOM 元素的焦点和模糊状态来处理菜单的状态。
document.querySelectorAll('.menu').forEach((menu) => {
const items = menu.querySelector('.menu-items');
menu.addEventListener('click', (e) => {
items.classList.remove("hide");
menu.focus(); // Probably redundant but just in case!
});
menu.addEventListener('blur', () => {
items.classList.add("hide");
});
});
.menu {
cursor: pointer;
display: inline-block;
}
.menu > span {
user-select: none;
}
.menu:focus {
outline: none;
border: none;
}
.hide {
display: none;
}
.menu-items > * {
user-select: none;
}
<div class="menu" tabindex="0">
<span>Menu +</span>
<div class="menu-items hide">
<div>Item 0</div>
<div>Item 1</div>
<div>Item 2</div>
</div>
</div>
这里的秘诀是给 .menu
div 一个 tabindex
以便它可以被浏览器聚焦。
JS 的首要任务:
- 在页面中搜索菜单的任何实例 class
document.querySelectorAll
- 获取当前被
.hide
隐藏的项目元素
- 当有人点击菜单时,从项目中删除
.hide
class- 这应该聚焦 div,但以防万一
menu.focus
被调用!
- 这应该聚焦 div,但以防万一
- 然后每当有人失去对 div 的关注时,AKA 点击离开等。添加
.hide
class 回来。
这可以扩展为更改按钮的文本,做各种其他事情。它还可以让你的代码超级干净,因为你依赖于浏览器自己的内部状态管理,所以你不必做任何检查。
处理第二次点击关闭
是的,它工作得很好,但是为了让它像大多数人期望的那样运行 UI 我们需要在再次单击菜单 div 时关闭它(替换 span
与任何你需要的 class):
...
menu.querySelector('span').addEventListener('click', (e) => {
e.stopPropagation();
menu.blur();
});
menu.addEventListener('click', (e) => {
items.classList.remove("hide");
menu.focus(); // Probably redundant but just in case!
});
menu.addEventListener('blur', () => {
items.classList.add("hide");
});
...
如果 event.CurrentTarget
不是汉堡菜单和文档中的任何其他内容(html 或 body),您可以从菜单中删除“打开”class被点击。
您还需要对 .hamburger
和 navLinks
本身进行 stopImmediatePropagation
以阻止这些点击被注册为对 body 的点击,因为它们是 [= body 的 33=],否则事件会冒泡到 body。
MDN 参考:https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles
const burger = document.querySelector(".burger");
const navLinks = document.querySelector(".header-nav-links");
const links = document.querySelectorAll(".header-nav-links li");
const body = document.querySelector('html');
//Toggle Nav
burger.addEventListener("click", (e) => {
navLinks.classList.toggle("open");
e.stopImmediatePropagation();
//Animate Links
links.forEach((link, index) => {
if (link.style.animation) {
link.style.animation = "";
}else{
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7+0.2}s`;
}
});
});
navLinks.addEventListener("click", (eve) => {
eve.stopImmediatePropagation();
});
body.addEventListener("click", (ev) => {
if (ev.currentTarget != burger) {
navLinks.classList.remove("open");
}
});
.burger {
display: block;
cursor:pointer;
}
.header-nav-links {
display: block;
}
.header-nav-links.open {
transform: translateX(0%);
}
.header-nav-links {
right: 0;
position: fixed;
height: 92vh;
top: 16vh;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
flex-direction: column;
align-items: center;
width: 50%;
transform: translateX(100%);
transition: transform 0.5s ease-in;
}
.header-nav-links li {
list-style-type: none;
}
.header-nav-links li:hover {
border: 1px solid #fff;
border-radius: 6pc;
background-color: #007bff;
}
.header-nav-links a {
color: whitesmoke;
text-decoration: none;
font-family: Arial, sans-serif;
font-weight: normal;
font-size: 16px;
border: 0px solid white;
transition: 400ms;
padding: 5px 15px;
border-radius: 19px;
}
<ul class="header-nav-links">
<li class="active"><a href="https://garibpathshala.in">HOME</a></li>
<li><a href="#projects_section">PROJECTS</a></li>
<li><a href="#meet_the_team_section">TEAM</a></li>
<li><a href="#about_us_section">ABOUT</a></li>
<li><a href="https://gallery.garibpathshala.in">GALLERY</a></li>
<li><a href="https://contact.garibpathshala.in">CONTACT</a></li>
<li><a href="https://donate.garibpathshala.in">DONATE</a></li>
<li><a href="https://join.garibpathshala.in">JOIN US</a></li>
</ul>
<div class="burger">
BURGER
<div line1></div>
<div line2></div>
<div line3></div>
</div>