共计 2878 个字符,预计需要花费 8 分钟才能阅读完成。
基本语法与参数解析
addEventListener是 DOM 元素上最常用的事件监听方法,完整签名如下:
element.addEventListener(type, listener[, options/useCapture]);

-
type:字符串类型,表示监听的事件类型(如
'click'、'scroll')。注意不要带on前缀(错误示例:onclick) -
listener:事件触发时执行的回调函数,接收一个 Event 对象参数。避免在此处直接写匿名函数(影响移除监听):
// 推荐:使用具名函数
function handleClick(e) {/*...*/}
btn.addEventListener('click', handleClick);
// 不推荐:匿名函数无法移除
btn.addEventListener('click', function() {/*...*/});
- options/useCapture:
- 布尔值:指定是否在捕获阶段触发(默认
false冒泡阶段) - 对象:可配置
capture、passive、once等属性
参数组合实战分析
1. 捕获与冒泡机制
// 示例:事件流阶段演示
document.querySelector('.outer').addEventListener('click', () => {console.log('冒泡阶段触发');
}, false);
document.querySelector('.inner').addEventListener('click', () => {console.log('捕获阶段触发');
}, true);
2. Passive 选项优化滚动性能
当监听 touch 或wheel事件时,设置 passive: true 可避免阻塞页面渲染:
// 旧方式(可能造成滚动卡顿)window.addEventListener('scroll', () => {/* 含有 preventDefault() 的逻辑 */
});
// 优化方案
window.addEventListener('scroll', () => {/* 不阻止默认行为 */}, {passive: true});
3. Once 选项实现单次监听
// 传统方式需要手动移除
function handleSubmit() {form.removeEventListener('submit', handleSubmit);
/* 提交逻辑 */
}
form.addEventListener('submit', handleSubmit);
// 使用 once 选项
form.addEventListener('submit', () => {/* 提交逻辑 */}, {once: true});
性能对比数据
通过 Chrome DevTools 的 Performance 面板测试:
| 参数配置 | 事件处理耗时(ms) | 主线程阻塞时间 |
|---|---|---|
| 默认配置 | 120 | 98 |
| {passive: true} | 45 | 12 |
| {capture: true} | 110 | 95 |
常见陷阱与解决方案
-
内存泄漏:未移除的事件监听
// 错误示例 class Component {constructor() {this.button.addEventListener('click', this.handleClick); } handleClick() { /*...*/} } // 正确方案 class Component {constructor() {this.handleClick = this.handleClick.bind(this); this.button.addEventListener('click', this.handleClick); } destroy() {this.button.removeEventListener('click', this.handleClick); } } -
事件重复绑定:相同回调多次注册
// 错误示例 button.addEventListener('click', () => console.log('Click 1')); button.addEventListener('click', () => console.log('Click 1')); // 防御方案 function addUniqueListener(element, type, callback) {element.removeEventListener(type, callback); element.addEventListener(type, callback); }
事件委托实战技巧
当需要处理动态元素或大量同类元素时:
// 传统方式(性能低)document.querySelectorAll('.item').forEach(item => {item.addEventListener('click', handleItemClick);
});
// 事件委托方案
document.querySelector('.container').addEventListener('click', (e) => {if (e.target.matches('.item')) {handleItemClick(e);
}
});
综合优化案例
优化前代码:
// 问题:匿名函数、缺少移除逻辑、未使用 passive
document.querySelectorAll('.draggable').forEach(el => {el.addEventListener('mousedown', () => {window.addEventListener('mousemove', () => {/*...*/});
window.addEventListener('mouseup', () => {// 忘记移除 move 监听!});
});
});
优化后方案:
// 使用具名函数 + 事件委托 +passive 优化
function handleDragStart(e) {if (!e.target.matches('.draggable')) return;
const onMove = throttle((e) => {/*...*/}, 16);
const onEnd = () => {window.removeEventListener('mousemove', onMove);
window.removeEventListener('mouseup', onEnd);
};
window.addEventListener('mousemove', onMove, { passive: true});
window.addEventListener('mouseup', onEnd, { once: true});
}
document.body.addEventListener('mousedown', handleDragStart);
思考与实践
检查你项目中的事件监听代码:
1. 是否存在可以替换为事件委托的场景?
2. 滚动 / 触摸事件是否添加了passive: true?
3. 单次执行的事件是否使用了 once 选项?
尝试用 Chrome 的 Performance 面板对比优化前后的性能差异。
正文完
发表至: 前端开发
近一天内
