如何通过在 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/