Home » Code » 关于JavaScript中的setCapture

关于JavaScript中的setCapture

在做元素拖动的代码里面,经常能见到el.setCapture()这些代码。之前不是很清楚,现在整理一下。

其实这最先是IE.5.5里面提出来的方法(IE当年就是屌),它的作用是将整个文档的鼠标事件捕获到某个对象上(Sets the mouse capture to the object that belongs to the current document)。看着不好理解,用我的话就是将某个对象的鼠标事件扩展到整个文档,要触发事件不限这个对象,整个文档都可以触发绑定到该对象上的鼠标事件。比如在某个div上绑定了点击事件,使用setCapture()之后,点击这个div外部也能触发这个点击事件。并且这个div外的元素乃至整个文档任何元素的自身的点击事件都“没了”,都变成了div的点击事件。

<div id="div">div</div>
<button id="btn">点我</button>

const div = document.getElementById('div');
const btn = document.getElementById('btn');

btn.onclick = function(e) {
	alert('btn');
}

div.setCapture();

div.addEventListener('click', function(e) {
	alert('click');
})

上边的例子,点击div外面的任何地方,都会弹出“click”,即使点击的是按钮[点我]或者浏览器的菜单栏,始终会弹出”click”。

但这个效果只有一次,弹出”click”后又恢复正常了,点击空白不会有任何弹出,点击按钮[点我]弹出的是”btn”。msdn文档中指出,鼠标点击事件会自动触发 onlosecapture 事件,姑且称为失去捕获事件。但我却无法监听这个事件,无论是使用div. onlosecapture 或者addEventListener都没法监听得到。文档中还指出要使捕获能多次使用,需要在点击事件的处理函数中再次执行setCapture,但我照做了,并没有得到期望的结果,依然是只有一次。添加一个事件在mousedown中执行setCapture,依然无法第二次弹出”alert”。看来,对于鼠标的click事件,是无法被多次捕获的了。

注意,以上全是在IE下的。Chrome中是没有setCapture这个方法的,直接报错!Firefox中有,但只对mousedown事件有效,也就是上边的代码在Firefox下不报错,但不会有点击空白会弹出”alert”的效果。MDN的文档说得更具体:

在处理一个 mousedown 事件过程中调用这个方法来把全部的鼠标事件重新定向到这个元素,直到鼠标按钮被释放或者document.releaseCapture()被调用。

这里明确指出,是在mousedown中调用,到鼠标松开之前,事件是会被定向到这个元素,其他情况下都是没有效果的。那么在mousedown到松开(mouseup/click)之前,能用的基本也就是mousemove了。其实用到setCapture的,也基本是做拖动时的mousemove了。因此上边的例子如果这么改:

div.addEventListener('mousedown', function(e) {
	div.setCapture();
})
div.addEventListener('mousemove', function(e) {
	console.log('move');
})

你会发现,在div上按下鼠标,接着移动鼠标即使鼠标移出了div甚至移出了浏览器可视范围也依然能打印出”move”。

上面这效果在IE和Firefox中有效,那Chrome中真的无法实现这种效果?答案是肯定的。或许你看到网上不是说标准DOM有个window.captureEvents/window.releaseEvents也是干这个的么?都是传而已,我实际试没看到效果,虽然不报错。MDN明确指出已被废弃。

那拖动要怎么做?因为给元素绑定mousemove事件要是拖快了鼠标不在元素上了就动不了了,必须得让鼠标出了元素依然有效啊,就是上边setCapture的效果。其实通俗的做法是给document绑定mousemove事件,这个也是能做到鼠标出了元素甚至出了浏览器可视范围也有效的!

var isDown = false;
div.addEventListener('mousedown', function(e) {
	isDown = true;
})
document.addEventListener('mousemove', function(e) {
	if (isDown) {
		console.log('move');
	}	
})
document.addEventListener('mouseup', function(e) {
	isDown = false;
})

结论:对于setCapture,还是把它凉一边吧。毕竟在三大浏览器下表现都不一致,”标准DOM”的方案captureEvents/releaseEvents也只是占坑压根没用!那开头怎么说在做拖动里边有setCapture,我觉得纯属多余,因为它代码里的mousemove就是绑定在document,这个已经能实现鼠标出了元素甚至浏览器可视范围仍然能拖动了,还要它来干啥?不懂,还请高手指教了。

参考链接:
https://msdn.microsoft.com/en-us/library/ms536742(v=vs.85).aspx
https://developer.mozilla.org/zh-CN/docs/Web/API/window/captureEvents

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Time limit is exhausted. Please reload CAPTCHA.