我到处搜索了一个很好的解决方案,但我找不到不使用jQuery 的解决方案。
是否存在跨浏览器,正常方式(没有奇怪的黑客或容易破解代码),检测元素外部的点击(可能有孩子也可能没有孩子)?
答案 0 :(得分:108)
向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>
答案 1 :(得分:25)
您需要在文档级别处理click事件。在事件对象中,您有一个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时,但其中一个父母是被监视的元素(我只是为此计算水平)。如果找到元素,您也可以使用boolean var,以便不从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);
}
};
这是跨浏览器兼容的代码,用于将事件监听器/处理程序(包括IE中的重写this
作为元素附加,就像jQuery为其事件处理程序所做的那样。有很多论点要考虑一些jQuery;)
答案 2 :(得分:6)
这个怎么样:
<强> jsBin demo 强>
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');
}
答案 3 :(得分:0)
要通过单击外部隐藏元素,我通常会应用这样简单的代码:
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);
答案 4 :(得分:0)
解决此问题的另一种非常简单快捷的方法是将 def create_from_images(tfrecord_dir, image_dir, shuffle):
print('Loading images from "%s"' % image_dir)
image_filenames = sorted(glob.glob(os.path.join(image_dir, '*')))
if len(image_filenames) == 0:
error('No input images found')
img = np.asarray(PIL.Image.open(image_filenames[0]))
resolution = img.shape[0]
channels = img.shape[2] if img.ndim == 3 else 1
if img.shape[1] != resolution:
error('Input images must have the same width and height')
if resolution != 2 ** int(np.floor(np.log2(resolution))):
error('Input image resolution must be a power-of-two')
if channels not in [1, 3]:
error('Input images must be stored as RGB or grayscale')
with TFRecordExporter(tfrecord_dir, len(image_filenames)) as tfr:
order = tfr.choose_shuffled_order() if shuffle else np.arange(len(image_filenames))
for idx in range(order.size):
img = np.asarray(PIL.Image.open(image_filenames[order[idx]]))
if channels == 1:
img = img[np.newaxis, :, :] # HW => CHW
else:
img = img.transpose([2, 0, 1]) # HWC => CHW
tfr.add_image(img)
def add_image(self, img):
if self.print_progress and self.cur_images % self.progress_interval == 0:
print('%d / %d\r' % (self.cur_images, self.expected_images), end='', flush=True)
if self.shape is None:
self.shape = img.shape
self.resolution_log2 = int(np.log2(self.shape[1]))
assert self.shape[0] in [1, 3]
assert self.shape[1] == self.shape[2]
assert self.shape[1] == 2**self.resolution_log2
tfr_opt = tf.python_io.TFRecordOptions(tf.python_io.TFRecordCompressionType.NONE)
for lod in range(self.resolution_log2 - 1):
tfr_file = self.tfr_prefix + '-r%02d.tfrecords' % (self.resolution_log2 - lod)
self.tfr_writers.append(tf.python_io.TFRecordWriter(tfr_file, tfr_opt))
assert img.shape == self.shape
for lod, tfr_writer in enumerate(self.tfr_writers):
if lod:
img = img.astype(np.float32)
img = (img[:, 0::2, 0::2] + img[:, 0::2, 1::2] + img[:, 1::2, 0::2] + img[:, 1::2, 1::2]) * 0.25
quant = np.rint(img).clip(0, 255).astype(np.uint8)
ex = tf.train.Example(features=tf.train.Features(feature={
'shape': tf.train.Feature(int64_list=tf.train.Int64List(value=quant.shape)),
'data': tf.train.Feature(bytes_list=tf.train.BytesList(value=[quant.tostring()]))}))
tfr_writer.write(ex.SerializeToString())
self.cur_images += 1
的数组映射到侦听器返回的事件对象中。如果您元素的path
或id
名称与数组中的元素名称之一匹配,则单击位于元素内部。
(如果您不想直接获取元素(例如class
,例如在ssr。中的reactjs / nextjs应用程序中,则此解决方案会很有用。)。
这里是一个例子:
document.getElementById('...')
我希望这个答案能对您有所帮助! (让我知道我的解决方案不是一个好的解决方案,或者您是否看到有待改进的地方。)
答案 5 :(得分:0)
为了找到更好的方法,我做了很多研究。 JavaScript方法.contains在DOM中递归检查其是否包含目标。我在react项目之一中使用了它,但是当react 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')
}
})