目录

  1. 事件流
    1. 事件冒泡
    2. 事件捕获
    3. 捕获和冒泡过程图
    4. addEventListener的第三个参数
    5. 事件代理
    6. 阻止冒泡的方法,阻止默认事件的方法(兼容IE)
      1. w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true
      2. 阻止默认事件
    7. DOM事件流

事件流

  描述的是从页面中接收事件的顺序。
  事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。

1
2
3
<div id="outer">
<p id="inner">Click me!</p>
</div>

上面的代码当中一个div元素当中有一个p子元素,如果两个元素都有一个click的处理函数,那么我们怎么才能知道哪一个函数会首先被触发呢?

事件冒泡

  冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。`
  事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。
  因此上面的例子在事件冒泡的概念下发生click事件的顺序应该是: p -> div -> body -> html -> document

事件捕获

  捕获型事件(event capturing):事件从最不精确的对象(document 对象)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定)
  上面的例子在事件捕获的概念下发生click事件的顺序应该是: document -> html -> body -> div -> p

  IE只支持事件冒泡,Chrome,Mozilla, Opera 7 和 Konqueror两种都支持,旧版本的Opera’s 和 iCab两种都不支持 。

捕获和冒泡过程图

冒泡和捕获

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!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>

  并且分别在obj1,obj2上绑定了一个点击事件,由于addEventListener的第三个参数为false,所以页面是在冒泡阶段处理绑定事件。此时整个页面可以有三种行为出现:

  1. 点击文字welcome时,弹出hello。
    此时就只触发了绑定在obj1上的点击事件。具体冒泡实现过程如下:welcome 属于文本节点,点击后,开始从文本节点查找,当前文本节点没有绑定点击事件,继续向上找,找到父级(id为obj1的div),有绑定的点击事件,执行,再向上找,body,没有绑定点击事件,再到html,document,都没再有绑定的点击事件,好,整个冒泡过程结束。
  2. 点击文字hello时,先弹出world,再弹出hello
  3. 点击world时,弹出hello。

addEventListener的第三个参数

lement.addEventListener(event, function, useCapture)

  第一个参数是需要绑定的事件,第二个参数是触发事件后要执行的函数。而第三个参数默认值是false,表示在事件冒泡的阶段调用事件处理函数,如果参数为true,则表示在事件捕获阶段调用处理函数。

  我们想要在点击每个h5标签时,弹出对应的innerHTML 。常规做法是遍历每个h5,然后在每个h5上绑定一个点击事件,这种做法在h5较少的时候可以使用,但如果有一万个h5,那就会导致性能降低。这时就需要事件代理出场了。

事件代理

  使用事件代理的好处不仅在于将多个事件处理函数减为一个,而且对于不同的元素可以有不同的处理方法

1
2
3
4
5
6
obj1.addEventListener('click',function(e){
var e=e||window.event;
if(e.target.nodeName.toLowerCase()=='h5'){
alert(e.target.innerHTML);
}
},false);

  由于事件冒泡机制,点击了h5后会冒泡到div,此时就会触发绑定在div上的点击事件,再利用target找到事件实际发生的元素,就可以达到预期的效果。

阻止冒泡的方法,阻止默认事件的方法(兼容IE)

w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true

阻止事件的传播兼容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function(e){
var e=e||window.event; // window.event 这是IE浏览器
e.stopPropagation();//阻止事件的传播(包括捕获也包括冒泡)

// 兼容处理
if (e && e.stopPropagation) { //这是其他浏览器
e.stopPropagation(); //阻止冒泡事件
e.preventDefault();//阻止默认事件
}else{
// IE浏览器
e.cancelBubble=true; //阻止冒泡事件
e.returnValue=false;//阻止默认事件
}

}

阻止冒泡事件的兼容性处理

1
2
3
4
5
6
7
function stopBubble(e) { 
if(e && e.stopPropagation) { //非IE
e.stopPropagation();
} else { //IE
window.event.cancelBubble = true;
}
}

阻止默认事件

  w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
  我们给一个dom同时绑定两个点击事件,一个用捕获,一个用冒泡,会执行几次事件,会先执行冒泡还是捕获?
首是进入捕获阶段,直到达到目标元素,再进入冒泡阶段

DOM事件流

  “DOM2级事件”规定的事件流包括三个阶段:事件捕获、处于目标阶段和事件冒泡阶段。发生的顺序是事件捕获阶段==>目标阶段==>事件冒泡阶段 DOM2级事件规定捕获阶段不会涉及目标事件

参考文章 终于弄懂了事件冒泡和事件捕获
浅谈事件冒泡与事件捕获