如何通过在 div 和 js 外部单击来关闭可扩展菜单
how to close expandable menu with click outside div and js
我在 js 中创建了一个可扩展的菜单。我将打开的 class 添加到单击事件中,然后再次单击以删除 class.
我想在菜单外单击时关闭菜单。我试过 document.addEventListener 但似乎不起作用。
function collapse() {
if (this.classList.contains('open')) {
this.classList.remove('open');
document.removeEventListener('click', collapse, false);
} else {
this.classList.add('open');
document.addEventListener('click', collapse, false);
}
}
var x = document.getElementsByClassName("toggle-menu");
for (var i = 0; i < x.length; i++) {
x[i].addEventListener('click', collapse, false);
}
这里是fiddlehttp://jsfiddle.net/4cv220mc/
谢谢
HTML
<div class="wrapper">
<div class="toggle-menu">
<h3 class="toggle-label">Menu</h3>
<ul class="toggle-body">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
</div>
Css -
.toggle-menu {
background-color:#ccc;
}
.toggle-body {
display:none;
}
.open .toggle-body {
display:block;
}
.wrapper {
width:100%;
height:500px;
}
JS -
function collapse() {
if (this.classList.contains('open')) {
this.classList.remove('open');
document.removeEventListener('click', collapse, false);
} else {
this.classList.add('open');
document.addEventListener('click', collapse, false);
}
}
var x = document.getElementsByClassName("toggle-menu");
for (var i = 0; i < x.length; i++) {
x[i].addEventListener('click', collapse, false);
}
var y = document.getElementsByClassName("wrapper");
for (var i = 0; i < y.length; i++) {
y[i].addEventListener('click', collapse, false);
}
我在这里编辑了你的 jsfiddle - http://jsfiddle.net/4cv220mc/5/
我注意到即使您单击菜单中的 li
元素之一,您发布的代码也会关闭菜单 - 这肯定是不可取的,因此最好检查一下折叠事件的目标点击和 close/open 只有当菜单的 h3
元素被点击时。
其次,您在这里寻找的是 event.stopPropagation
功能。这样你就可以把事件监听器放在 toggle-menu
class 和 document.documentElement
上——这就是 html
元素(你可以把它放在 document.body 上但是有时不会填满屏幕)。
因此菜单事件侦听器将切换 open
class 并停止进一步传播。 html 事件侦听器 - 现在仅当菜单事件侦听器 而不是 首先被调用时才会被调用 - 将检查是否有任何打开的菜单,如果有,将删除open
class 来自所有人。
这样您就可以拥有尽可能多的菜单,点击任何打开的菜单(包括另一个菜单)以外的任何地方都可以将其关闭。
这是您的 jsFiddle 的 JS 代码(我使用 [].slice.call
将所有菜单元素转换为数组,以便我可以对它们调用 forEach 函数,我发现这更容易 read/write , 但你的反击方法当然也有效):
var html = document.documentElement,
menus = [].slice.call(document.getElementsByClassName('toggle-menu'));
function menuClick(e){
e.stopPropagation();
if(e.target.tagName.toLowerCase()=='h3'){
this.classList.toggle('open');
}
var openMenus = document.querySelectorAll('.open.toggle-menu');
for(var i=0;i<openMenus.length;i++){
if(openMenus[i]==this)
continue;
openMenus[i].classList.remove('open');
}
}
function bodyClick(e){
if(document.querySelector('.toggle-menu.open')){
menus.forEach(function(menu){
if(menu.classList.contains('open'))
menu.classList.remove('open');
});
}
}
menus.forEach(function(menu){
menu.addEventListener('click',menuClick,false);
});
html.addEventListener('click',bodyClick,false);
如果您想同时打开菜单(单击一个不应关闭其他菜单),您可以在 menuClick
函数中删除从 var openMenus
开始的部分。
这是一个fiddle:http://jsfiddle.net/811r0nzw/
我在 js 中创建了一个可扩展的菜单。我将打开的 class 添加到单击事件中,然后再次单击以删除 class.
我想在菜单外单击时关闭菜单。我试过 document.addEventListener 但似乎不起作用。
function collapse() {
if (this.classList.contains('open')) {
this.classList.remove('open');
document.removeEventListener('click', collapse, false);
} else {
this.classList.add('open');
document.addEventListener('click', collapse, false);
}
}
var x = document.getElementsByClassName("toggle-menu");
for (var i = 0; i < x.length; i++) {
x[i].addEventListener('click', collapse, false);
}
这里是fiddlehttp://jsfiddle.net/4cv220mc/
谢谢
HTML
<div class="wrapper">
<div class="toggle-menu">
<h3 class="toggle-label">Menu</h3>
<ul class="toggle-body">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
</div>
Css -
.toggle-menu {
background-color:#ccc;
}
.toggle-body {
display:none;
}
.open .toggle-body {
display:block;
}
.wrapper {
width:100%;
height:500px;
}
JS -
function collapse() {
if (this.classList.contains('open')) {
this.classList.remove('open');
document.removeEventListener('click', collapse, false);
} else {
this.classList.add('open');
document.addEventListener('click', collapse, false);
}
}
var x = document.getElementsByClassName("toggle-menu");
for (var i = 0; i < x.length; i++) {
x[i].addEventListener('click', collapse, false);
}
var y = document.getElementsByClassName("wrapper");
for (var i = 0; i < y.length; i++) {
y[i].addEventListener('click', collapse, false);
}
我在这里编辑了你的 jsfiddle - http://jsfiddle.net/4cv220mc/5/
我注意到即使您单击菜单中的 li
元素之一,您发布的代码也会关闭菜单 - 这肯定是不可取的,因此最好检查一下折叠事件的目标点击和 close/open 只有当菜单的 h3
元素被点击时。
其次,您在这里寻找的是 event.stopPropagation
功能。这样你就可以把事件监听器放在 toggle-menu
class 和 document.documentElement
上——这就是 html
元素(你可以把它放在 document.body 上但是有时不会填满屏幕)。
因此菜单事件侦听器将切换 open
class 并停止进一步传播。 html 事件侦听器 - 现在仅当菜单事件侦听器 而不是 首先被调用时才会被调用 - 将检查是否有任何打开的菜单,如果有,将删除open
class 来自所有人。
这样您就可以拥有尽可能多的菜单,点击任何打开的菜单(包括另一个菜单)以外的任何地方都可以将其关闭。
这是您的 jsFiddle 的 JS 代码(我使用 [].slice.call
将所有菜单元素转换为数组,以便我可以对它们调用 forEach 函数,我发现这更容易 read/write , 但你的反击方法当然也有效):
var html = document.documentElement,
menus = [].slice.call(document.getElementsByClassName('toggle-menu'));
function menuClick(e){
e.stopPropagation();
if(e.target.tagName.toLowerCase()=='h3'){
this.classList.toggle('open');
}
var openMenus = document.querySelectorAll('.open.toggle-menu');
for(var i=0;i<openMenus.length;i++){
if(openMenus[i]==this)
continue;
openMenus[i].classList.remove('open');
}
}
function bodyClick(e){
if(document.querySelector('.toggle-menu.open')){
menus.forEach(function(menu){
if(menu.classList.contains('open'))
menu.classList.remove('open');
});
}
}
menus.forEach(function(menu){
menu.addEventListener('click',menuClick,false);
});
html.addEventListener('click',bodyClick,false);
如果您想同时打开菜单(单击一个不应关闭其他菜单),您可以在 menuClick
函数中删除从 var openMenus
开始的部分。
这是一个fiddle:http://jsfiddle.net/811r0nzw/