4、事件对象详解:event对象属性、target与currentTarget区别、阻止默认行为
事件对象,说白了就是浏览器在触发事件时,悄悄塞给你的那个「情报包」。你写监听函数时那个 e 或 event 参数,里面装满了谁触发了事件、在哪个元素上、鼠标坐标、按键状态等等信息。我刚开始学前端时,总觉得这玩意儿就是个黑盒子,直到有一次排查线上 bug,才真正把它摸透了。
4.1 event 对象的核心属性
先列几个我几乎每天都在用的属性。你不需要全背下来,但下面这张表里的,建议你记牢。
| 属性名 | 类型 | 说明 |
|---|---|---|
type |
String | 事件类型,比如 "click"、"keydown" |
target |
Element | 触发事件的原始元素 |
currentTarget |
Element | 绑定监听器的元素 |
eventPhase |
Number | 事件流阶段:1捕获、2目标、3冒泡 |
preventDefault() |
Function | 阻止默认行为 |
stopPropagation() |
Function | 阻止事件继续传播 |
stopImmediatePropagation() |
Function | 阻止传播 + 阻止同元素其他监听器 |
isTrusted |
Boolean | 是否由用户真实操作触发 |
console.log(e),然后展开看所有属性。比翻文档快多了。
4.2 target 与 currentTarget 的区别
这是面试高频题,也是实际开发中容易踩坑的地方。我直接给你看代码,比讲概念清楚。
<div id="parent">
<button id="child">点我</button>
</div>
<script>
document.getElementById('parent').addEventListener('click', function(e) {
console.log('target:', e.target.id); // 输出: child
console.log('currentTarget:', e.currentTarget.id); // 输出: parent
});
</script>
为什么会这样?因为事件冒泡了。你点的是按钮,但监听器绑在父元素上。
- target:永远指向触发事件的源头元素。就是你实际点到的那个。
- currentTarget:指向当前正在处理事件的元素。也就是你绑定监听器的那个。
我遇到过一个问题:有同事在事件委托里用 e.target === e.currentTarget 做判断,结果发现永远不相等。嗯,因为 target 是子元素,currentTarget 是父元素,当然不相等。正确的做法是用 e.target.matches('.some-selector') 来过滤。
this 指向 currentTarget,而不是 target。这是很多新手容易搞混的。
4.3 事件流阶段与 eventPhase
事件流分三个阶段:捕获 → 目标 → 冒泡。你可以通过 e.eventPhase 拿到当前处于哪个阶段。值分别是 1、2、3。
我画了一张图,帮你把这三个阶段和 target、currentTarget 的关系理清楚。
4.4 阻止默认行为:preventDefault()
有些元素天生自带「默认行为」。比如点击 <a> 会跳转链接,点击 <form> 里的提交按钮会刷新页面,右键会弹出菜单。这些行为有时候不是你想要的。
e.preventDefault() 就是用来干掉这些默认行为的。注意,它不会阻止事件传播,只阻止浏览器执行默认动作。
// 阻止链接跳转
document.querySelector('a').addEventListener('click', function(e) {
e.preventDefault();
console.log('链接被拦截,不会跳转');
});
// 阻止表单提交
document.querySelector('form').addEventListener('submit', function(e) {
e.preventDefault();
// 这里可以做 AJAX 提交
});
e.preventDefault() 但没生效。查了半天发现,我监听的是 click 事件,而右键菜单的默认行为是在 contextmenu 事件里触发的。记住:preventDefault() 只对当前事件类型有效。
4.5 阻止事件传播:stopPropagation() 与 stopImmediatePropagation()
这两个方法容易混淆。我直接给你对比。
| 方法 | 作用 | 是否阻止同元素其他监听器 |
|---|---|---|
stopPropagation() |
阻止事件继续向上(或向下)传播 | 否 |
stopImmediatePropagation() |
阻止传播 + 阻止当前元素上其他监听器执行 | 是 |
// 场景:同一个元素绑了两个监听器
btn.addEventListener('click', function(e) {
console.log('第一个监听器');
e.stopImmediatePropagation(); // 第二个不会执行
});
btn.addEventListener('click', function(e) {
console.log('第二个监听器'); // 这行不会输出
});
我个人建议:除非你非常清楚自己在做什么,否则尽量少用 stopPropagation()。因为它会破坏事件委托机制。我见过一个项目,全局用了大量 stopPropagation(),结果导致下拉菜单、弹窗关闭等功能全部失灵。嗯,排查起来相当痛苦。
4.6 实用技巧:事件对象解构
写监听函数时,我习惯把需要的属性解构出来,代码更干净。
// 不推荐
element.addEventListener('click', function(e) {
const type = e.type;
const target = e.target;
const x = e.clientX;
const y = e.clientY;
});
// 推荐
element.addEventListener('click', function({ type, target, clientX, clientY }) {
console.log(type, target, clientX, clientY);
});
(e: MouseEvent) => {},这样编辑器会给你自动补全属性。
4.7 本章小结
事件对象是 UI 事件驱动的核心情报站。你需要记住:
target是源头,currentTarget是当前处理者preventDefault()阻止默认行为,不阻止传播stopPropagation()阻止传播,但不阻止同元素其他监听器stopImmediatePropagation()是更狠的版本,连兄弟监听器都干掉
这些概念搞清楚了,后面讲事件委托、自定义事件、手势系统时,你才能游刃有余。