一、两种事件流的由来
要解释由来,先要解释一下为什么会产生事件流。
当浏览器发展到第四代时,浏览器开发团队发现一个问题,当你单击一个按钮的时候,你也单击了按钮的容器元素,还单击了整个页面。这就有意思了,当你给按钮和按钮的容器元素都添加了点击事件,那么是哪个的点击事件先执行呢?
针对这个问题,就产生了事件流——描述的是页面中接收事件的顺序。但是当时的不同的开发团队提出了两种完全相反的事件流,IE提出的事件流是:事件冒泡流,而Netscape Communicator提出的事件流是:事件捕获流。
二、事件冒泡和事件捕获的区别
1、事件冒泡:事件开始由最具体的元素接收,然后逐级向上传播到较不具体的节点(document)
2、事件捕获:事件开始由不太具体的节点接收,然后逐级向下传播到最具体的节点
W3C把这两种事件流整合在了一起变成了DOM事件流,DOM事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,然后是目标接收到事件,最后一个阶段是冒泡阶段。
-
在支持W3C标准的浏览器上,就可以使用addEventListener()方法来指定在什么阶段调用事件处理程序。使用方法为:
element.addEventListener(event, funciton, userCapture)
可以通过设置userCapture的值来选择使用什么事件流,值为false时,事件是在事件冒泡时执行;值为true时,事件在事件捕获时执行。默认为false。 -
但在不支持W3C标准的浏览器上,如IE可以使用
attachEvent(event, function)
方法,IE的事件模型默认是在事件冒泡时执行的,不能设置为事件捕获流。
所以把addEventListener()的userCapture设置为false是比较安全的,也实现了浏览器的兼容。
总结下来就是:
- 事件捕获阶段:事件从最上一级标签开始往下查找,直到捕获到事件目标(target)
- 事件冒泡阶段:事件从事件目标(target)开始,往上冒泡直到页面的最上一级标签
假设有一个div元素中有一个子元素p
<div id="parent">
<p id="child">这是文本</p>
</div>
当两个元素都绑定了点击事件,如果用户点击了p,它在div和p上都触发了click事件,那这两个事件处理程序哪个先执行呢?事件顺序是什么?
在冒泡阶段调用事件处理程序时
document.getElementById("parent").addEventListener("click",function(e){
alert("parent元素被点击");
},false);
document.getElementById("child").addEventListener("click",function(e){
alert("child元素被点击");
},false);
结果:
child元素被点击
parent元素被点击
结论:当你使用事件冒泡时,子级元素先触发,父级元素后触发,即p先触发,div后触发。
在事件捕获阶段调用事件处理程序时:
document.getElementById("parent").addEventListener("click",function(e){
alert("parent元素被点击");
},true);
document.getElementById("child").addEventListener("click",function(e){
alert("child元素被点击");
},true);
结果:
parent元素被点击
child元素被点击
结论:当你使用事件捕获时,父级元素先触发,子级元素后触发,即div先触发,p后触发。
ps:使用传统绑定事件方式也是采用的事件冒泡方式。
如 element.οnclick=function(){}
三、stopPropagation()和preventDefault()作用
有时候我们需要阻止事件的向上冒泡
- 在支持W3C标准下:使用
event.stopPropagation()
- 在IE下:设置
cancelBubble = true
有时候我们需要取消事件的默认行为
- 在支持W3C标准下:使用e
vent.preventDefault()
- 在IE下:设置
window.event.returnValue = false