addEventListener 第三个参数
addEventListener 第三个参数的两种形式及使用场景
问题
addEventListener 函数的第三个参数有什么作用?
解答
addEventListener 的第三个参数有两种形式:布尔值或配置对象。
基本语法
element.addEventListener(type, listener, useCapture)
element.addEventListener(type, listener, options)
事件流回顾
理解第三个参数前,需要了解事件流的三个阶段:
捕获阶段 → 目标阶段 → 冒泡阶段
(从外到内) (目标元素) (从内到外)
布尔值形式
// 第三个参数为布尔值,表示是否在捕获阶段触发
// 默认为 false(冒泡阶段触发)
const outer = document.getElementById('outer')
const inner = document.getElementById('inner')
// 冒泡阶段触发(默认)
outer.addEventListener('click', () => {
console.log('outer - 冒泡')
}, false)
// 捕获阶段触发
outer.addEventListener('click', () => {
console.log('outer - 捕获')
}, true)
inner.addEventListener('click', () => {
console.log('inner')
})
// 点击 inner 时输出顺序:
// outer - 捕获
// inner
// outer - 冒泡
配置对象形式
element.addEventListener('click', handler, {
capture: false, // 是否在捕获阶段触发,默认 false
once: false, // 是否只触发一次,默认 false
passive: false, // 是否为被动监听器,默认 false
signal: null // AbortSignal,用于移除监听器
})
once - 只触发一次
const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
console.log('只会触发一次')
}, { once: true })
// 等价于
btn.addEventListener('click', function handler() {
console.log('只会触发一次')
btn.removeEventListener('click', handler)
})
passive - 被动监听器
// passive: true 告诉浏览器不会调用 preventDefault()
// 浏览器可以立即滚动,不用等待 JS 执行完毕
// 常用于优化滚动性能
document.addEventListener('touchstart', (e) => {
// 这里不能调用 e.preventDefault()
console.log('touch start')
}, { passive: true })
// 滚动事件优化
window.addEventListener('scroll', () => {
// 处理滚动
}, { passive: true })
signal - 使用 AbortController 移除监听
const controller = new AbortController()
document.addEventListener('click', () => {
console.log('clicked')
}, { signal: controller.signal })
// 移除监听器
controller.abort()
// 适合批量移除多个监听器
const controller2 = new AbortController()
element.addEventListener('click', handler1, { signal: controller2.signal })
element.addEventListener('keydown', handler2, { signal: controller2.signal })
element.addEventListener('scroll', handler3, { signal: controller2.signal })
// 一次性移除所有监听器
controller2.abort()
完整示例
<!DOCTYPE html>
<html>
<body>
<div id="outer" style="padding: 50px; background: #eee;">
outer
<div id="inner" style="padding: 30px; background: #ddd;">
inner
</div>
</div>
<script>
const outer = document.getElementById('outer')
const inner = document.getElementById('inner')
// 捕获阶段
outer.addEventListener('click', () => {
console.log('1. outer 捕获')
}, { capture: true })
// 冒泡阶段
outer.addEventListener('click', () => {
console.log('4. outer 冒泡')
}, { capture: false })
inner.addEventListener('click', () => {
console.log('2. inner 捕获')
}, { capture: true })
inner.addEventListener('click', () => {
console.log('3. inner 冒泡')
}, { capture: false })
// 点击 inner 输出:
// 1. outer 捕获
// 2. inner 捕获
// 3. inner 冒泡
// 4. outer 冒泡
</script>
</body>
</html>
关键点
- 第三个参数可以是布尔值(是否捕获)或配置对象
capture: true在捕获阶段触发,false(默认)在冒泡阶段触发once: true让事件只触发一次,自动移除监听器passive: true提升滚动性能,但不能调用preventDefault()signal配合AbortController可以方便地批量移除监听器
目录