冒泡阶段和捕获阶段

想象一个场景:在纸上画三个同心圆,然后手指按住最内层的同心圆。此时你只按住了最内层的同心圆吗?

当然不是,你的手指同时按住了三个圆。

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("弹出我,我把上面的覆盖了");}; //以这个为准