事件的冒泡&捕获
事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。
<div id="dv1">
<div id="dv2">
<div id="dv3">click</div>
</div>
</div>
比如上图的代码,三个div标签呈嵌套关系,假使三个元素都注册了相同的事件,那么他们的触发顺序是怎样的呢?
故此,为了解决这个事件流问题,微软和网景提出了两种几乎相反的概念。
事件的冒泡
微软提出了名为事件冒泡的事件流。事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。可以想象把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。
针对于刚才的例子,三个元素间的触发顺序就应该是 dv3->dv2->dv1.
我们给上面代码里的三个div元素绑定事件,触发一下看看
var dv1 = document.getElementById('dv1')
var dv2 = document.getElementById('dv2')
var dv3 = document.getElementById('dv3')
dv1.onclick = function(){
console.log(this.id)
}
dv2.onclick = function(){
console.log(this.id)
}
dv3.onclick = function(){
console.log(this.id)
}
点击
dv1
时,控制台输出dv1
点击
dv2
时,控制台输出dv2
和dv1
点击
dv3
时,控制台输出dv3
和dv2
和dv1
这就是事件冒泡。
事件的捕获
网景提出另一种事件流名为事件捕获。事件从最不精确的对象(document 对象)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定),与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。同样形象的比喻一下可以想象成警察逮捕屋子内的小偷,就要从外面一层层的进入到房子内。
针对刚才的例子,三个元素间的触发顺序应该是dv1->dv2->dv3。
我们给上面代码里的三个div元素绑定事件,触发一下看看:
var dv1 = document.getElementById('dv1')
var dv2 = document.getElementById('dv2')
var dv3 = document.getElementById('dv3')
dv1.addEventListener('click',f1,true)
dv2.addEventListener('click',f1,true)
dv3.addEventListener('click',f1,true)
function f1(){
console.log(this.id)
}
element.addEventListener(event, function, useCapture)
addEventListener
方法用来为一个特定的元素绑定一个事件处理函数,是JavaScript中的常用方法,其传入三个参数,分别是‘没有on的事件类型’,‘事件处理函数’,‘控制事件阶段’,第三个参数是boolean类型,默认是false,表示在事件冒泡的阶段调用事件处理函数,像上图中传入true,就表示在事件捕获的阶段调用事件处理函数。
点击dv1
时,控制台输出dv1
点击dv2
时,控制台输出dv1
和dv2
点击dv3
时,控制台输出dv1
和dv2
和dv3
这就是事件捕获。
冒泡和捕获过程
事件捕获和事件冒泡属于两个相反的过程,这里可以有一个我感觉十分恰当的比喻,当你把一个可以漂浮在水面上的物品,使劲向水里砸下去,它会首先有一个下降的过程,这个过程就可以理解为从最顶层向事件发生的最具体元素(目标点)的捕获过程;之后由于浮力大于物体自身的重力,物体会在到达最低点( 最具体元素)之后漂浮到水面上,这个过程相对于事件捕获是一个回溯的过程,即事件冒泡。
<!DOCTYPE html>
<html>
<head>
<title>event</title>
</head>
<body>
<div id="obj1">
welcome
<h5 id="obj2">hello</h5>
<h5 id="obj3">world</h5>
</div>
<script type="text/javascript">
var obj1=document.getElementById('obj1');
var obj2=document.getElementById('obj2');
obj1.addEventListener('click',function(){
alert('hello');
},false);
obj2.addEventListener('click',function(){
alert('world');
})
</script>
</body>
</html>
如上所示,这是一个十分简单地文档结构:document > html > body > div > h5
并且分别在obj1,obj2上绑定了一个点击事件
由于addEventListener的第三个参数为false,所以页面是在冒泡阶段处理绑定事件。此时整个页面可以有三种行为出现
- 点击文字welcome时,弹出hello。此时就只触发了绑定在obj1上的点击事件。具体冒泡实现过程如下:welcome 属于文本节点,点击后,开始从文本节点查找,当前文本节点没有绑定点击事件,继续向上找,找到父级(id为obj1的div),有绑定的点击事件,执行,再向上找,body,没有绑定点击事件,再到html,document,都没再有绑定的点击事件,好,整个冒泡过程结束。
- 点击文字hello时,先弹出world,再弹出hello。
- 点击world时,弹出hello。
阻止冒泡
event.stopPropagation()
$("#div1").mousedown(function(event){
event.stopPropagation();
});
return false
$("#div1").mousedown(function(event){
return false;
});
但是这两种方式是有区别的。
return false
不仅阻止了事件往上冒泡,而且阻止了事件本身。event.stopPropagation()
则只阻止事件往上冒泡,不阻止事件本身。