冒泡阶段和捕获阶段
想象一个场景:在纸上画三个同心圆,然后手指按住最内层的同心圆。此时你只按住了最内层的同心圆吗?
当然不是,你的手指同时按住了三个圆。
JavaScript 也有类似的规定。两个 div 嵌套,内层 div 中放置一个 button 按钮,点击这个按钮之后,并
不仅触发了这个按钮的点击事件,而且也触发了两个 div 的点击事件,甚至也触发了 body 和 window 的点击事
件。例如:
<!DOCTYPE html>
<html>
<head>
<meta charset=UTF-8>
<title>document</title>
</head>
<body>
<div id="box1">
<div id="box2">
<button id="btn">按我</button>
</div>
</div>
<script type="text/javascript">
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var btn = document.getElementById("btn");
btn.onclick = function(){
alert("按钮被点击");
}
box1.onclick = function(){
alert("box1 被点击");
}
box2.onclick = function(){
alert("box2 被点击");
}
</script>
</body>
</html>
打开浏览器查看页面。点击按钮,按钮、box1、box2 的事件处理函数都触发了,弹出了 3 个对话框,如图
11-1 所示。也就是说,当我们点击按钮的时候,浏览器认为我们也点击了 box1 和 box2。
那么到底应该是谁先触发事件呢?是最外层的元素还是最内层的元素呢?JavaScript 规定了一个事件的
传播方向,称为事件流。事件流依次有两个阶段:事件捕获阶段、事件冒泡阶段。
事件捕获阶段:从外层元素往内层传播
事件冒泡阶段:从内层元素往外层传播
也就是说,JavaScript 规定了事件要先从最外层的元素 window 对象开始触发,依次向内层传播,这个阶
段称为“事件捕获阶段”,如同捕鱼的时候,大网总是最先捕获到鱼。所以 window 对象先捕获了事件,然后
document 捕获到了事件,然后 document.body 捕获到了事件,然后是 box1、box2、button 捕获到了事件;
当事件传播到最内层的 button 元素之后,重新向外层开始传播,这个阶段称为“事件冒泡阶段”,如同池塘里
的气泡,会慢慢往上跑。事件又一次的冒泡到 box2、box1、document.body、document、window 对象。
DOM0 级事件监听
目前为止,我们一直在用 oDiv.onclick=function(){}这样的语法添加事件监听,被称为 DOM0 级事件监
听。DOM 标准历经几次修订,我们把最原始的标准称为 DOM0 级标准。
DOM0 级事件监听,只能监听事件的冒泡阶段。
我们来做实验:
<!DOCTYPE html>
<html>
<head>
<meta charset=UTF-8>
<title>document</title>
</head>
<body>
<div id="box1">
<div id="box2">
<button id="btn">按我</button>
</div>
</div>
<script type="text/javascript">
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var btn = document.getElementById("btn");
btn.onclick = function(){
alert("按钮被点击");
}
box1.onclick = function(){
alert("box1 被点击");
}
box2.onclick = function(){
alert("box2 被点击");
}
document.body.onclick = function(){
alert("body 被点击");
}
document.onclick = function(){
alert("document 被点击");
}
window.onclick = function(){
alert("window 被点击");
}
</script>
</body>
</html>
我们给按钮、box1、box2、body、document、window 对象添加了 onclick 事件。
我们发现,事件处理的函数执行顺序,是从最内层依次到最外层的。事件按这个方向传播:先是最内层的按
钮,然后是 box2,接下来是 box1、body、document 和 window 对象。事件处理函数的书写顺序,和触发的先
后没有任何关系。
但是千万不要认为事件就是从最内层往最外层传播的。这只不过是因为 DOM0 级事件监听有局限性,它只能
监听到事件的冒泡阶段。也就是说,事件从最外层往最内层传播的事件捕获阶段,它无法监听到。
DOM0 级事件监听还有一个特点,同一个对象的同名事件会相互覆盖,后写的监听会覆盖先写的监听。比如:
box1.onclick = function(){alert("我弹不出来");};
box1.onclick = function(){alert("弹出我,我把上面的覆盖了");}; //以这个为准