JavaScript(js)事件冒泡、事件捕获、事件委托详解
1.什么是事件
JavaScript和HTML之间的交互是通过事件实现的。事件,就是文档或浏览器窗口发生的一些特定的交互瞬间。可以使用监听器(或事件处理程序)来预定事件,以便事件发生时执行相应的代码。通俗的说,这种模型其实就是一个观察者模式。(事件是对象主题,而这一个个的监听器就是一个个观察者)
2.事件流
事件流描述的是从页面中接收事件的顺序。而IE和Netscape提出了完全相反的事件流概念。IE事件流是事件冒泡,而Netscape的事件流就是事件捕获。

3.事件冒泡
IE的事件流叫做事件冒泡。即事件开始时由最具体的元素(当前元素target,文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。所有现代浏览器都支持事件冒泡,并且会将事件一直冒泡到window对象。
4.事件捕获
事件捕获的思想是不太具体的节点(最外层dom节点)应该更早的接收到事件,而在最具体的节点(target节点)应该最后接收到事件。事件捕获的用以在于事件到达预定目标之前捕获它。IE9+、Safari、Chrome、Opera和Firefox支持,且从window开始捕获(尽管DOM2级事件规范要求从document)。由于老版本浏览器不支持,所以很少有人使用事件捕获。

5.DOM事件流
“DOM2级事件”规定事件流包括三个阶段,事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的事件捕获,为截获事件提供了机会。然后是实际的目标接收了事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

6.示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JavaScript事件测试
<style>
#a{
width: 300px;
height: 300px;
background: pink;
}
#b{
width: 200px;
height: 200px;
background: blue;
}
#c{
width: 100px;
height: 100px;
background: yellow;
}
</style>
</head>
<body>
<div id="a">a
<div id="b">b
<div id="c">c</div>
</div>
</div>
<script>
window.onload=function(){
var a = document.getElementById("a"),b = document.getElementById("b"),c = document.getElementById("c");
c.addEventListener("click", function (event) {
console.log("c1");
// 注意第三个参数没有传进 false , 因为默认传进来的是 false
//,代表冒泡阶段调用,个人认为处于目标阶段也会调用的
});
c.addEventListener("click", function (event) {
console.log("c2");
}, true);
b.addEventListener("click", function (event) {
console.log("b");
}, true);
a.addEventListener("click", function (event) {
console.log("a1");
}, true);
a.addEventListener("click", function (event) {
console.log("a2")
});
a.addEventListener("click", function (event) {
console.log("a3");
event.stopImmediatePropagation();
}, true);
a.addEventListener("click", function (event) {
console.log("a4");
}, true);
}
</script>
</body>
</html>
• 点击b或者c,控制台输出a1、a3
stopImmediatePropagation包含stopPropagation的功能,它会阻止同一事件的所有传播功能(捕获或者冒泡),stopPropagation只阻止同一事件对应的那个函数,同一事件的其他绑定函数不会阻止。由于stopImmediatePropagation阻止该元素上后来绑定的事件处理程序被调用以不输出 a4。因为事件捕获被拦截了,自然不会触发 b、c 上的事件,所以不输出 b、c1、c2,冒泡更谈不上了,所以不输出 a2。
• 点击c,控制台输出a1、a2、a3
点击c时,此时事件流是处于目标阶段,不是冒泡阶段、也不是捕获阶段,事件处理程序被调用的顺序是注册的顺序(代码的书写顺序)。不论你指定的是true还是false。换句话来说就是现在点击的是a这个盒子本身,它处于事件流的目标状态,而既非冒泡,又非捕获。(需要注意的是,此时的eventPhase为2,说明事件流处于目标阶段。当点击a的时候,先从document捕获,然后一步步往下找,找到a这个元素的时候,此时的target和currentTarget是一致的,所以认定到底了,不需要再捕获了,此时就按顺序执行已经预定的事件处理函数,执行完毕后再继续往上冒泡…)
• 事件流原则(实际测试过)
(1)从外向内,先是捕获阶段,在目标(target)阶段,最后是冒泡阶段
(2)非target节点元素,先捕获后冒泡
(3)Target节点元素,按代码书写顺序(事件的注册顺序)执行(无论是冒泡还是捕获)
7.事件委托
事件委托又叫时间代理,指的是利用事件的冒泡原理,只给外层的父容器添加监听事件,若子元素有点击事件,则会冒泡到父容器上,即子元素委托父元素代为执行事件。
• 事件委托的应用场景
例如,一个播放列表有成千上万首歌曲,如果遍历播放列表,给每个 item 添加点击事件,而这样无疑会多次访问 DOM,每次 DOM 操作都会引起浏览器的重绘与重排,非常不利于性能优化。
这时候我们可以利用事件委托,只给最外层的容器添加响应事件,这样只需一次 DOM 操作,就能达到目的。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>事件委托测试例子(runoob.com)</title>
</head>
<body>
<ul id="music">
<li>青花瓷</li>
<li>东风破</li>
<li>双节棍</li>
<li>追梦</li>
<li>破红尘</li>
<li>沙漠骆驼</li>
<li>我是一只小小鸟</li>
<li>探清水河</li>
<li>典狱司</li>
<li>江湖少年</li>
<li>狂浪生</li>
<li>盛世回收</li>
</ul>
<script>
window.onload=function(){
var $music = document.getElementById('music');
$music.addEventListener('click', function(e) {
if(e.target.nodeName.toLowerCase() === 'li') { // 判断目标元素target是否为li元素
var content = e.target.innerHTML;
console.log(content);
}
}, false)
}
</script>
</body>
</html>
本文深入解析JavaScript中的事件流概念,包括事件冒泡、事件捕获和事件委托等关键机制。通过实例演示不同阶段事件处理的顺序及如何利用事件委托优化性能。
907

被折叠的 条评论
为什么被折叠?



