问题:
如果一个元素和它的祖先元素注册了同一类型的事件函数(例如点击等), 那么当事件发生时事件函数调用的顺序是什么呢?
比如, 考虑如下嵌套的元素:
<div class="outer">
<div class="inner"></div>
</div>
两个元素都有onclick的处理函数. 如果用户点击了inner, inner和outer上的事件处理函数都会被调用. 但谁先谁后呢?
事件流:
事件流描述的是从页面中接受事件的顺序,但有意思的是,微软(IE)和网景(Netscape)开发团队居然提出了两个截然相反的事件流概念,IE的事件流是事件冒泡流(event bubbling),而Netscape的事件流是事件捕获流(event capturing)。
事件捕获(event capturing)
网景公司提出的事件流叫事件捕获流。
事件捕获流的思想是不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件,针对上面同样的例子,点击按钮,那么此时click事件会按照这样传播:(下面我们就借用addEventListener的第三个参数来模拟事件捕获流)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<button>
<p>点击捕获</p>
</button>
</div>
<script>
var oP=document.querySelector('p');
var oB=document.querySelector('button');
var oD=document.querySelector('div');
var oBody=document.querySelector('body');
oP.addEventListener('click',function(){
console.log('p标签被点击')
},true);
oB.addEventListener('click',function(){
console.log("button被点击")
},true);
oD.addEventListener('click', function(){
console.log('div被点击')
},true);
oBody.addEventListener('click',function(){
console.log('body被点击')
},true);
</script>
</body>
</html>
同样我们看一下后台的打印结果:
正如我们看到的,它是和冒泡流万全相反,从最不具体的元素接收到最具体的元素接收事件 body=>div=>button=>p
事件冒泡(event bubbling)
与事件捕获相反, 事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body onclick="bodyClick()">
<div onclick="divClick()">
<button onclick="btn()">
<p onclick="p()">点击冒泡</p>
</button>
</div>
<script>
function p(){
console.log('p标签被点击')
}
function btn(){
console.log("button被点击")
}
function divClick(event){
console.log('div被点击');
}
function bodyClick(){
console.log('body被点击')
}
</script>
</body>
</html>
点击p元素打印结果:
DOM事件流:
DOM事件分0级和2级:
1、0级分两种:
- 行内事件:在标签中写事件
<input type="button" id="btn" value="按钮" onclick="alert('123')">
- 元素.on事件名=函数
document.getElementById("btn").onclick = function () {
alert('123');
}
事件没有1级DOM
2、DOM2级事件:添加和移除事件处理程序:addEventListener()和removeEventListener()。
DOM2级(addEventListener)事件流包含3个阶段,事件捕获阶段、处于目标阶段、事件冒泡阶段。 先(document)开始一路向下捕获, 直到达到目标元素, 其后再次从目标元素开始冒泡.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button id="btn">DOM事件流</button>
<script>
var btn=document.getElementById("btn");
btn.onclick=function(event){
console.log("div 处于目标阶段");
};
document.body.addEventListener("click",function(event){
console.log("event bubble 事件冒泡");
},false);
document.body.addEventListener("click",function(event){
console.log("event catch 事件捕获");
},true);
</script>
</body>
</html>
打印结果为:
就是这样一个流程,先捕获,然后处理,然后再冒泡出去。
作为开发者, 可以决定事件处理器是注册在捕获或者是冒泡阶段. 如果addEventListener的最后一个参数是true, 那么处理函数将在捕获阶段被触发; 否则(false)默认冒泡, 会在冒泡阶段被触发.
阻止冒泡事件:
w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true
参考链接: 浅谈js的事件冒泡和事件捕获