通过使用纯Javascript单击页面上的任意位置来关闭元素
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过使用纯Javascript单击页面上的任意位置来关闭元素相关的知识,希望对你有一定的参考价值。
我有一个菜单打开一个子导航点击一个标题,我试图通过点击页面上的任何一个除了一个打开的元素关闭。
我的代码片段如下:
function showSubMenu(show, hide1, hide2, hide3, hide4) {
document.getElementById(show).className = "subNavShow";
document.getElementById(hide1).className = "subNavHide";
document.getElementById(hide2).className = "subNavHide";
document.getElementById(hide3).className = "subNavHide";
document.getElementById(hide4).className = "subNavHide";
}
.subNavHide {
display: none;
}
.subNavShow {
display: block;
}
<ul class="topnavList" id="siteTopnavList">
<li>
<a onclick="showSubMenu('text1','text2','text3','text4','text5')" href="javascript:void(0);">Nav 1</a>
<article id="text1" class="subNavHide">
<ul>
<li><a href="#">Sub Nav 1</a></li>
</ul>
</article>
</li>
<li>
<a onclick="showSubMenu('text2','text1','text3','text4','text5')" href="javascript:void(0);">Nav 2</a>
<article id="text2" class="subNavHide"> text2 </article>
</li>
<li>
<a onclick="showSubMenu('text3','text1','text2','text4','text5')" href="javascript:void(0);">Nav 3</a>
<article id="text3" class="subNavHide"> text3 </article>
</li>
<li>
<a onclick="showSubMenu('text4','text1','text2','text3','text5')" href="javascript:void(0);">Nav 4</a>
<article id="text4" class="subNavHide"> text4 </article>
</li>
<li>
<a onclick="showSubMenu('text5','text1','text2','text3','text4')" href="javascript:void(0);">Nav 5</a>
<article id="text5" class="subNavHide"> text5 </article>
</li>
</ul>
理想情况下,我想使用纯Javascript,但如果Jquery绝对必要,那么我也可以
在我看来,使用当前实现执行此操作的最简单方法是向文档添加单击事件侦听器,并使用.closest
确定单击的元素是否为打开的元素:
document.addEventListener(`click`, hideSubMenus);
function hideSubMenus(event) {
if (!event.target.closest(`.topnavList li a, .subNavShow`)) {
document.getElementById(`text1`).className = `subNavHide`;
document.getElementById(`text2`).className = `subNavHide`;
document.getElementById(`text3`).className = `subNavHide`;
document.getElementById(`text4`).className = `subNavHide`;
document.getElementById(`text5`).className = `subNavHide`;
}
}
然而,closest
与旧版浏览器不兼容:https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
但我可能会在链接中添加类并向其添加事件侦听器,而不是使用“onclick”属性。这样,例如,如果您将“subNavLink”类添加到每个链接,您可以使用循环来处理链接,而不是为每个链接重复相同的行:
let links, i, n;
links = document.getElementsByClassName(`subNavLink`);
for (i = 0, n = links.length; i < n; i++) {
links[i].addEventListener(`click`, showSubMenu);
}
function showSubMenu(event) {
let currentLink, i, link, n;
currentLink = event.currentTarget;
for (i = 0, n = links.length; i < n; i++) {
link = links[i];
if (link === currentLink) {
// this link was clicked, so we have to show its submenu
link.nextElementSibling.className = `subNavShow`;
} else {
// this link was not clicked, so we have to hide its submenu
link.nextElementSibling.className = `subNavHide`;
}
}
}
通过这样做,您可以将hideSubMenus
函数更改为:
function hideSubMenus(event) {
let i, n;
if (!event.target.closest(`.subNavLink, .subNavShow`)) {
for (i = 0, n = links.length; i < n; i++) {
links[i].nextElementSibling.className = `subNavHide`;
}
}
}
我发现最简单的方法就是在菜单下面创建一个图层(或者更常见的是模态窗口)。然后使用该图层作为元素来测试它是否已被点击(相对于坐在它上面的元素)。
(该示例使用灰色背景来显示叠加层的存在,但它可以很容易地成为透明DIV并且仍然具有相同的效果)
// Get the elements that will show/hide
const overlay = document.getElementById('overlay');
const menu = document.getElementById('menu');
// Change the className to have the CSS that will hide
// the elements
// Since the 'menu' element is on top of the 'overlay'
// element, clicking on the 'menu' should not click
// through the 'overlay' -- thus ignoring this section
// of code to hide things
overlay.onclick = function(){
menu.className = 'hide';
overlay.className = 'hide';
};
// Quick and dirty code to reset the page and display
// the 'menu' and 'overlay' DIVs
function open(){
menu.className = '';
overlay.className = '';
}
#overlay{
display: block;
position: fixed;
top: 0; left: 0;
height: 100%; height: 100vh;
width: 100%; width: 100vw;
background-color: rgba( 0, 0, 0, 0.25 );
}
#overlay.hide{ display: none; }
#menu{
position: absolute;
background-color: white;
padding: 15px; border-radius: 5px;
}
#menu ul, #menu li{
margin: 0; padding: 0;
list-style: none;
}
#menu.hide{ display: none; }
<a href="javascript:open();">OPEN</a>
<div id="overlay"></div>
<div id="menu">
<ul>
<li>Menu Item</li>
<li>Menu Item</li>
<li>Menu Item</li>
<li>Menu Item</li>
</ul>
</div>
代码越通用越好。
使用文档上设置的eventListener
,您可以在页面上收听所有“点击”事件(在DOM树中冒泡)。您可以关闭所有article
s,无论如何,然后在适当的时候显示点击的条目(及其祖先)。
下面的代码,但有很多好处:
- 它是动态的。这意味着它可以处理任何数量的子级别。
article
元素既不需要id
属性也不需要在第一次渲染时显示/隐藏类。代码变得松散耦合。 - 只有一个处理函数将存储在内存中,而不是每个菜单项一个。
- 它将处理稍后(在
eventListener
注册后)添加到菜单中的条目。 - 您的代码是分解的,这使得它更易于阅读和重用。
let topNavList = document.querySelector('#siteTopnavList');
document.addEventListener('click', function (e) {
let t = e.target;
// At this point, close menu entries anyway
topNavList.querySelectorAll('a ~ article').forEach(el => {
el.classList.add('subNavHide'); el.classList.remove('subNavShow');
});
// Drop clicks on the "active" link or any element that is outside the `#siteTopnavList` menu
if (!t.nextElementSibling || t.nextElementSibling.classList.contains('subNavShow')) {
return;
}
if (t.nodeName.toLowerCase() == 'a' && topNavList.contains(t)) {
topNavList.querySelectorAll('article').forEach(x => {
if(x.contains(t) || x === t.nextElementSibling) {
x.classList.remove('subNavHide');
x.classList.add('subNavShow');
}
});
// Prevent the browser to process the anchor href attribute
e.preventDefault();
}
});
#siteTopnavList article {display:none}
#siteTopnavList .subNavShow {display:block}
<ul class="topnavList" id="siteTopnavList">
<li>
<a href="#">Nav 1</a>
<article>
<ul>
<li><a href="#">Sub Nav 1</a></li>
</ul>
</article>
</li>
<li>
<a href="#">Nav 2</a>
<article> TEXT2 </article>
</li>
<li>
<a href="#">Multi level</a>
<article>
<ul>
<li>
<a href="#">Sub Nav 1</a>
<article>
<ul>
<li><a href="http://nowhere.com">Deep 1</a></li>
<li><a href="http://nowhere.com">Deep 2</a></li>
<li>
<a href="#">Even deeper 3</a>
<article>
<ul>
<li><a href="#">Even deeper 1</a></li>
</ul>
</article>
</li>
</ul>
</article>
</li>
</ul>
</article>
</li>
</ul>
以上是关于通过使用纯Javascript单击页面上的任意位置来关闭元素的主要内容,如果未能解决你的问题,请参考以下文章
通过Javascript自动单击具有唯一标签和类名的页面上的按钮的方法
单击屏幕上的任意位置关闭所有 Angular JS Bootstrap 弹出窗口?