在了解什么是js中的事件流之前,我们先了解一下什么是js的事件。引用W3c的解释
HTML事件就是发生在HTML元素上的事情 当在HTML中使用javaScript时,javaScript能够应对这些事件
举例几个常用的事件:
知道了什么是事件,那什么是事件流呢?
我们先从字面意义上理解,事件我们已经知道了是什么,那流呢?我们看看百度对于流的解释

那连着事件我们是不是就能将事件流理解为从页面接收事件的顺序,这些事件连起来就形成了一个像液体一样的整体,这个整体中的事件又有着自己的执行顺序,这就是事件流。
在事件流中又有着两个模型
这里我们引用一张图,以便于理解事件流模型

当节点事件被触发时,会由内圈到外圈 div-->body-->html-->document 依次触发其祖先节点的同类型事件,直到DOM根节点
当节点事件被触发时,会从DOM根节点开始,依次触发其子孙节点的同类型事件,直到当前节点自身。由外圈到内圈 document-->html-->body-->div
事件冒泡是由IE提出的,而事件捕获则是由Netscape(网景)提出的事件流概念。
后来ECMAScript将两种模型进行了整合,制定了统一的标准:先捕获在冒泡

现在整合后的标准事件流就有了三个阶段:
Tips: DOM2级事件规定了在捕获阶段不会涉及到目标阶段事件,但在IE9、Safari、Chrome、Firefox和Opera9.5及更高版本都会在捕获阶段触发目标事件上的事件
addEventListener() 方法用于向指定元素添加事件句柄。
参数 | 描述 |
|---|---|
event | 必须,字符串,指定事件名 |
function | 必须,指定事件触发时要执行的函数 |
useCapture | 可选值,指定事件是否在捕获或者冒泡阶段执行;1、true:事件句柄在捕获阶段执行;2、- false- 默认值,事件句柄在冒泡阶段执行 |
<div id="btn1" style="height: 150px;width: 150px;background: red;color: #fff">
btn1
<div id="btn2" style="height: 100px;width: 100px;background: green;color: #fff">
btn2
<div id="btn3" style="height: 50px;width: 50px;background: blue;color: #fff">
btn3
</div>
</div>
</div>
<script>
let btn1 = document.getElementById('btn1')
let btn2 = document.getElementById('btn2')
let btn3 = document.getElementById('btn3')
btn1.addEventListener('click',function (){
alert('btn1')
},true)
btn2.addEventListener('click',function (){
alert('btn2')
},true)
btn3.addEventListener('click',function (){
alert('btn3')
},true)
</script>
当我们点击btn3时让我们看看这段代码的执行情况

当我们点击btn2时让我们看看这段代码的执行情况

因为addEventListener()最后一个参数我们设置的true,所以整个执行过程按捕获进行。从当前的Dom根节点开始,直到当前节点自身。
还是同样的代码,只是这里我们将addEventListener()的最后一个参数改为false,将执行过程设置为冒泡。
Tips:因为addEventListener()最后一个参数默认就是false(冒泡),所以不写也可以
<div id="btn1" style="height: 150px;width: 150px;background: red;color: #fff">
btn1
<div id="btn2" style="height: 100px;width: 100px;background: green;color: #fff">
btn2
<div id="btn3" style="height: 50px;width: 50px;background: blue;color: #fff">btn3</div>
</div>
</div>
<script>
let btn1 = document.getElementById('btn1')
let btn2 = document.getElementById('btn2')
let btn3 = document.getElementById('btn3')
btn1.addEventListener('click',function (){
alert('btn1')
},false)
btn2.addEventListener('click',function (){
alert('btn2')
},false)
btn3.addEventListener('click',function (){
alert('btn3')
},false)看一下执行情况

这里能看到执行顺序是先执行节点自身事件,而后向上执行其祖先节点的同类型事件,直到Dom根节点。
stopPropagation() 方法
防止调用相同事件的传播。 传播意味着向上冒泡到父元素或向下捕获到子元素。
我们在btn2上使用stopPropagation()函数
<div id="btn1" style="height: 150px;width: 150px;background: red;color: #fff">
btn1
<div id="btn2" style="height: 100px;width: 100px;background: green;color: #fff">
btn2
<div id="btn3" style="height: 50px;width: 50px;background: blue;color: #fff">btn3</div>
</div>
</div>
<script>
let btn1 = document.getElementById('btn1')
let btn2 = document.getElementById('btn2')
let btn3 = document.getElementById('btn3')
btn1.addEventListener('click',function (){
alert('btn1')
},false)
btn2.addEventListener('click',function (event){
event.stopPropagation()
alert('btn2')
},false)
btn3.addEventListener('click',function (){
alert('btn3')
},false)
</script>看一下执行情况

可以看到我们在点击btn3时冒泡执行至btn2后就进行了终止
事件委托又叫事件代理,指的是利用事件冒泡原理,只需给外层父容器添加事件,若内层子元素有点击事件,则会冒泡到父容器上,这就是事件委托,简单说就是:子元素委托它们的父级代为执行事件。
这种情况的应用场景在什么地方呢?举个例子:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>我们现在有一个这样的列表,我想监听所有的<li>标签,<li>标签我这里只列了五个,但实际业务中这里有可能会循环出成千上万个<li>标签。如果我们给每个<li>都绑定事件,会极大的影响页面性能,这个时候我们就可以使用事件委托来进行优化。
let ulDom = document.getElementsByTagName('ul')
ulDom[0].addEventListener('click', function(event){
alert(event.target.innerHTML)
})我们看一下执行情况

可以看到,我们通过事件委托给每一个<li>标签都添加了点击事件
我们总结一下事件委托的优化: