参考文章:https://javascript.info/bubbling-and-capturing
一.事件冒泡和事件捕获的理解
事件冒泡:事件到达目标节点后,会沿着捕获阶段的路线返回。同样,所有经过的节点都会触发对应的事件
事件捕获:当一个事件触发后,从window对象出发,不断经过下级节点,直到抵达最终目标节点(event.target)。在事件达到目标节点之前就是事件捕获的phrase。所有经过的节点都会触发该事件。
下图为表格<table>的点击行元素<td>时,事件捕获阶段--抵达目标阶段--冒泡阶段:
我们很少需要在事件捕获阶段进行操作,所以通常会忽略掉这个阶段。
elem.
type,listener,useCapture
addEventListener(
)
- type: 必须,String类型,事件类型
- listener: 必须,函数体或者JS方法
- useCapture: 可选,boolean类型。指定事件是否发生在捕获阶段。默认为false,事件发生在冒泡阶段。
<body>
<div id="fa"></div>
<script>
// 事件冒泡
let fa = document.getElementById('fa');
window.addEventListener("click",function(e){console.log("window")})
document.addEventListener("click",function(e){console.log("document")})
document.body.addEventListener("click",function(e){console.log("body")})
fa.addEventListener("click",function(e){console.log("fa");})
//元素触发事件的打印顺序就是 fa body document window
</script>
</body>
二.事件代理(委托)
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
利用elem.
elem元素下实际发生该事件的子级,例如addEventListener()函数可以得到
<div id="fa">
<div id="son1">son1</div>
<div id="son2">son2</div>
<div id="son3">son3</div>
</div>
<script>
let fa = document.getElementById('fa');
fa.addEventListener("click",function(e){
console.log(e.target);
})
//点击son1时,打印结果为<div id="son1">son1</div>
//点击son2时,打印结果为<div id="son2">son2</div>
//点击son3时,打印结果为<div id="son3">son3</div>
</script>
事件委托的优点:
(1)(比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件。) 提高性能:每一个函数都会占用内存空间。每一个事件都会与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多;如果使用事件委托,只需添加一个事件处理程序代理所有事件,所占用的内存空间更少。
(2)动态监听:使用事件委托可以自动绑定动态添加的元素,即新增的节点不需要主动添加也可以一样具有和其他元素一样的事件。
三.阻止事件冒泡
event.stopPropagation();可以阻止事件冒泡
冒泡的使用是很便捷的。英文文档明确说明,没有必要的需求不要阻止冒泡!因为使用event.stopPropagation();阻止冒泡可能会给后续的工作留下一些隐患,如后面需求需要父级监听子级的事件发生来做分析判断,但我们又阻止了冒泡,父级无法就无法监听该事件。
其实没有需求真的需要阻止冒泡,如果一个需求表面上看起来需要阻止冒泡才能实现,那么我们可以想想其他方法来解决这个问题。一个方法是自定义事件。还有一个方法是利用事件代理,如三层嵌套元素祖父级,父级和子级,现在我的需求是点击子级打印"son",点击父级打印"father"。所以我们可以在祖父级元素上使用elem.addEventListener()方法监听e.target,即可以根据得到元素判断处理触发事件。