我到处都在寻找一个好的解决方案,但我找不到一个不使用 jQuery的解决方案。
是否有跨浏览器的正常方式(没有奇怪的黑客或容易破解的代码)来检测元素外部的点击(可能有也可能没有孩子)?
我到处都在寻找一个好的解决方案,但我找不到一个不使用 jQuery的解决方案。
是否有跨浏览器的正常方式(没有奇怪的黑客或容易破解的代码)来检测元素外部的点击(可能有也可能没有孩子)?
添加一个事件侦听器document
并使用它Node.contains()
来查找事件的目标(即最内层单击的元素)是否在您指定的元素内。它甚至可以在 IE5 中使用
var specifiedElement = document.getElementById('a');
//I'm using "click" but it works with any event
document.addEventListener('click', function(event) {
var isClickInside = specifiedElement.contains(event.target);
if (!isClickInside) {
//the click was outside the specifiedElement, do something
}
});
var specifiedElement = document.getElementById('a');
//I'm using "click" but it works with any event
document.addEventListener('click', function(event) {
var isClickInside = specifiedElement.contains(event.target);
if (isClickInside) {
alert('You clicked inside A')
} else {
alert('You clicked outside A')
}
});
div {
margin: auto;
padding: 1em;
max-width: 6em;
background: rgba(0, 0, 0, .2);
text-align: center;
}
Is the click inside A or outside?
<div id="a">A
<div id="b">B
<div id="c">C</div>
</div>
</div>
您需要在文档级别处理单击事件。在事件对象中,您有一个target
属性,即被单击的最内层 DOM 元素。有了这个,你可以检查它自己并沿着它的父元素直到文档元素,如果它们之一是你的观察元素。
请参阅jsFiddle上的示例
document.addEventListener("click", function (e) {
var level = 0;
for (var element = e.target; element; element = element.parentNode) {
if (element.id === 'x') {
document.getElementById("out").innerHTML = (level ? "inner " : "") + "x clicked";
return;
}
level++;
}
document.getElementById("out").innerHTML = "not x clicked";
});
与往常一样,由于 addEventListener/attachEvent,这不是跨坏浏览器兼容的,但它的工作原理是这样的。
一个孩子被点击,当不是 event.target 时,但它的父母之一是被监视的元素(我只是为此计算级别)。如果找到或未找到元素,您也可能有一个布尔变量,以不从 for 子句中返回处理程序。我的示例仅限于处理程序仅在没有匹配项时完成。
添加跨浏览器兼容性,我通常是这样做的:
var addEvent = function (element, eventName, fn, useCapture) {
if (element.addEventListener) {
element.addEventListener(eventName, fn, useCapture);
}
else if (element.attachEvent) {
element.attachEvent(eventName, function (e) {
fn.apply(element, arguments);
}, useCapture);
}
};
这是跨浏览器兼容的代码,用于附加事件侦听器/处理程序,包括this
在 IE 中的重写,作为元素,就像 jQuery 为其事件处理程序所做的那样。有很多论据要记住一些 jQuery ;)
How about this:
document.onclick = function(event){
var hasParent = false;
for(var node = event.target; node != document.body; node = node.parentNode)
{
if(node.id == 'div1'){
hasParent = true;
break;
}
}
if(hasParent)
alert('inside');
else
alert('outside');
}
为了找到更好的方法,我做了很多研究。JavaScript 方法 .contains 在 DOM 中递归地检查它是否包含目标。我在一个反应项目中使用了它,但是当反应 DOM 在设置状态下发生变化时,.contains 方法不起作用。所以我想出了这个解决方案
//Basic Html snippet
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="mydiv">
<h2>
click outside this div to test
</h2>
Check click outside
</div>
</body>
</html>
//Implementation in Vanilla javaScript
const node = document.getElementById('mydiv')
//minor css to make div more obvious
node.style.width = '300px'
node.style.height = '100px'
node.style.background = 'red'
let isCursorInside = false
//Attach mouseover event listener and update in variable
node.addEventListener('mouseover', function() {
isCursorInside = true
console.log('cursor inside')
})
/Attach mouseout event listener and update in variable
node.addEventListener('mouseout', function() {
isCursorInside = false
console.log('cursor outside')
})
document.addEventListener('click', function() {
//And if isCursorInside = false it means cursor is outside
if(!isCursorInside) {
alert('Outside div click detected')
}
})
解决此问题的另一种非常简单快捷的方法是将数组映射path
到侦听器返回的事件对象中。如果您的元素的id
orclass
名称与数组中的其中一个匹配,则单击在您的元素内。
(如果您不想直接获取元素,此解决方案可能很有用(例如:document.getElementById('...')
,例如在 reactjs/nextjs 应用程序中,在 ssr..)。
这是一个例子:
document.addEventListener('click', e => {
let clickedOutside = true;
e.path.forEach(item => {
if (!clickedOutside)
return;
if (item.className === 'your-element-class')
clickedOutside = false;
});
if (clickedOutside)
// Make an action if it's clicked outside..
});
我希望这个答案对你有帮助!(如果我的解决方案不是一个好的解决方案,或者您发现有什么需要改进的地方,请告诉我。)
要通过在元素外部单击来隐藏元素,我通常会应用这样简单的代码:
var bodyTag = document.getElementsByTagName('body');
var element = document.getElementById('element');
function clickedOrNot(e) {
if (e.target !== element) {
// action in the case of click outside
bodyTag[0].removeEventListener('click', clickedOrNot, true);
}
}
bodyTag[0].addEventListener('click', clickedOrNot, true);