IE和W3C事件的传播,IE世界的大独裁者setCapture()

        如果采用内联式事件绑定,如下:

<style>
#div1{background:#ccc;}
#div2{background:#999;}
#div3{background:#666;}
</style>
<div id="div1" onclick="alert(1)">1
	<div id="div2" onclick="alert(2)">2
		<div id="div3" onclick="alert(3)">3</div>
	</div>
</div>

        你只点击id为div3背景色为#666的区域,在弹出内容为3的警告框后,接着弹出内容为2,然后为1的警告框,事件由当前的目标对象逐渐向父级层层传播,也就是沿着DOM树一直向上到document对象(也可能是window对象),我们称它为冒泡。并且因为采用了内联式事件绑定,这个现象在IE和W3C浏览器的表现都是一致的,不存在兼容性问题。

        但是,如果事件的绑定采用的是“非侵扰式”的,既IE的attachEvent和W3C的addEventListener,便有了兼容问题,除了具体的js方法的不同外,事件的传播方式也有了区别。

        W3C的事件传播是同时包含“捕获”和“冒泡”阶段的,但是IE却只有“冒泡”阶段。

        W3C的“捕获”阶段和“冒泡”正好相反,它是从document对象(也可能是window对象),逐渐向目标元素传播,也就是上例的代码假如使用“非侵扰式事件绑定”并且绑定在了捕获阶段,那么点击div3后,警告框要从最外层alert(1)开始最后到alert(3),并且先进行“捕获”阶段,再进行“冒泡”阶段,2个阶段都要执行。

        addEventListener在绑定事件处理程序时,可以设置选择事件在哪个阶段被捕获,addEventListener()有三个参数,第一个为事件类型,第二个为执行方法,第三个是一个布尔值,true表示事件在捕获阶段绑定,false表示在冒泡阶段绑定

        IE是没有捕获阶段的,为此它用setCapture()和releaseCapture()方法来弥补。

        可是,捕获阶段存在的意义是什么呢?

        捕获阶段的存在意味着我们可以控制事件的执行顺序。如果只有冒泡阶段,事件只能从目标元素向上逐层传播,我们可以阻止冒泡,阻止传播,却不能改变事件的执行顺序。如开头的实例代码,执行顺序只能是先alert(3),最后alert(1),我们可以阻止alert(3)之后的警告框的弹出,但只要执行就不能改变从3到1的顺序。

        IE是如何用setCapture()弥补自身这个缺陷的呢?

        IE中,假设id为div1一个元素设置了setCapture(),然后再绑定事件,比如一个点击事件,接下来,无论点击哪里,被触发的都是div1元素的点击事件了,甚至你点击浏览器的地址栏,工具栏,都会触发div1的点击事件()。此时你的ie浏览器会像中毒一样,点击关闭按钮都是在触发div1的点击事件(),其他元素不管在div之上还是之下的,他们绑定的点击事件全部失效。

        这样你就决定了在点击操作后,在所有绑定了点击事件的元素中,谁最优先执行自己的方法了,并且在div1点击后执行的代码里你可以继续设置其他元素的setCapture,然后你又决定了第二个执行的元素了。setCapture()就像一个土匪一样,他不是来弥补IE缺失的捕获阶段,而是连原来的冒泡也打得稀碎了,然后强行规定一个元素的绑定事件作为第一优先级,并且独占触发事件,屏蔽其他元素的同类事件触发。

        但是,这种特权是可以被设置和取消的,所以setCapture()就像一个独裁者的王冠一样,谁戴了谁就是唯一的优先级,其他同类全部作废,然后你可以决定这顶王冠给谁。有setCapture()的世界没有冒泡,更没有捕获,而是随意你给谁带上setCapture()王冠谁就变成唯一的主角。你通过给元素设置setCapture()的先后顺序来自由改变这个“事件世界”的秩序,感觉就像老美一样。

    实例代码(此段代码中是document对象设置了setCapture,请在IE下测试):

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>html5</title>
<link rel="stylesheet" type="text/css" href="">
</head>
<body>
<div id="div1">
	Hello World
</div>

<script type="text/javascript">
document.body.setCapture();
div1.attachEvent('onclick',function(){
	div1.innerHTML+='<br>div1 click'
});	
document.attachEvent('onclick',function(){
	div1.innerHTML+='<br> Body click';
})
</script>	
</body>
</html>


        虽然IE下的setCapture()强大到可以屏蔽掉浏览器的关闭按钮,但是它也很脆弱(这也如同现实世界的真正独裁者一样,如萨达姆、卡扎菲),就是对话框和上下文菜单会终止setCapture()。比如你设置了div1的setCapture(),你鼠标点呐点,不管点谁都像点击div1一样,可是当突然鼠标右键弹出小菜单后,会发现setCapture()瞬间失效,事件又开始遵循“冒泡”的秩序传播了。

       补充说明严格来说,点击浏览器的地址栏,工具栏,甚至点击浏览器关闭按钮都会触发div1的点击事件这个现象在ie6下表现最为明显,ie10则是在第一次预览或刷新后第一次点击地址栏,工具栏才会有这种现象,点击之后setCapture也失效了,冒泡重新成为唯一的秩序,地址栏,工具栏什么的也恢复正常。多数ie核心浏览器光标离开浏览器的body区域,进入工具栏的区域操作之后,setCapture也是很容易就失效了。setCapture这么容易失效,我们用着岂不是很不放心?setCapture有一个onlosecapture事件会在setCapture失效时触发,这样你就掌控了所有可能会发生的状态,从而做出应对决策


你可能感兴趣的:(冒泡,attachEvent,捕获,setCapture())