dom事件流分为三个阶段:捕获阶段、目标阶段、冒泡阶段
事件捕获:事件从DOM顶层节点触发,逐级往下传播至最具体的元素接收的过程
事件冒泡:事件从最具体的元素触发,逐级向上传播至DOM数的最顶层节点的过程
目标元素:具体触发事件的元素
当事件被触发后这三个阶段的顺序是这样的:捕获阶段,目标阶段,冒泡阶段
一般我们在dom事件上只需要一个阶段就行了(即捕获阶段还是冒泡阶段),通过 addEventListener 这个方法的第三个参数,默认为false:冒泡阶段;true:捕获阶段
冒泡:
<div id="father" class="father">
<div id="son" class="son"></div>
</div>
<script>
const son = document.getElementById('son')
const father = document.getElementById('father')
father.addEventListener(
'click',
function () {
console.log('i am father')
},
false
)
son.addEventListener(
'click',
function () {
console.log('i am son')
},
false
)
</script>
点击son盒子
捕获:
<div id="father" class="father">
<div id="son" class="son"></div>
</div>
<script>
const son = document.getElementById('son')
const father = document.getElementById('father')
father.addEventListener(
'click',
function () {
console.log('i am father')
},
true
)
son.addEventListener(
'click',
function () {
console.log('i am son')
},
true
)
</script>
点击son盒子
我们还可以通过冒泡原理来实现事件委托(事件委派、事件代理)
事件委托:我们不直接给子元素设置事件侦听器,而是直接给父元素设置事件侦听器,然后利用冒泡的原理影响到每个子元素
还是通过addEventListener这个方法实现的
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
const ul = document.getElementById('ul')
ul.addEventListener(
'click',
function () {
if (e.target.tagName !== 'LI') return
console.log('i am li')
},
'li'
)
点击每个li盒子
事件委托的好处:
- 减少给dom元素绑定的事件数量,提高性能(事件处理函数是一个函数,复杂数据类型,需要开辟一个内存空间存储这个函数,如果给每个子元素设置事件处理函数,就需要开辟多个内存空间来存储多个事件处理函数)
- 如果添加了一个li元素,新添加的元素仍然可以触发该事件
- 减少dom操作,不必访问子元素节点