JavaScript 的事件机制是前端交互的核心,它允许开发者监听用户行为(如点击、输入、滚动等)并作出响应。理解原生 JS 的事件模型对于写出高效、可维护的代码至关重要。
本文将从以下几个方面详细讲解 原生 JavaScript 的事件机制:
一、什么是事件?
在浏览器中,事件(Event) 是当某些行为发生时由浏览器自动触发的通知。例如:
- 用户点击按钮 → 触发
click
事件; - 鼠标移动到元素上 → 触发
mouseover
事件; - 表单提交 → 触发
submit
事件;
开发者可以通过编写"事件监听器"来响应这些事件。
二、绑定事件的方式
1. DOM 0 级事件处理(内联 / 属性式)
html
<!-- HTML 内联 -->
<button onclick="sayHello()">点我</button>
js
// JS 属性绑定
const btn = document.getElementById('btn');
btn.onclick = function() {
alert('Hello');
};
⚠️ 缺点:
- 只能绑定一个事件处理器;
- 不利于代码分离;
2. DOM 2 级事件处理(推荐方式)
使用 addEventListener()
方法添加多个监听器,并支持捕获/冒泡阶段。
js
btn.addEventListener('click', function(event) {
console.log('按钮被点击了');
}, false);
参数说明:
参数 | 含义 |
---|---|
'click' |
事件类型(如 click、keydown、input 等) |
function(event) |
回调函数,接收事件对象 |
false |
是否在捕获阶段执行,默认为 false(即冒泡阶段) |
✅ 优点:
- 支持多个监听器;
- 支持捕获和冒泡;
- 更加灵活、安全;
三、事件流的三个阶段
当你点击页面中的某个元素时,事件并不是直接触发一次,而是经历以下三个阶段:

1. 捕获阶段
事件从最外层 (如 window
或 document
)向目标元素逐级传递。
css
window → document → html → body → ... → target
在这个阶段,父级元素可以提前"感知"到事件即将到达目标。
2. 目标阶段
事件到达你真正点击的目标元素 。此时 event.target
被确定。
3. 冒泡阶段
事件从目标元素开始,向外层传播回根节点。
css
target → ... → body → html → document → window
⚠️ 并非所有事件都会冒泡(如
focus
,blur
就不会冒泡)。
四、useCapture 参数的作用
addEventListener()
的第三个参数控制监听器是在哪个阶段执行:
true
:在捕获阶段执行;false
(默认):在冒泡阶段执行;
js
document.getElementById('parent').addEventListener('click', () => {
console.log('Parent');
}, true);
document.getElementById('child').addEventListener('click', () => {
console.log('Child');
}, false);
点击 <div id="child">
输出顺序:
Parent
Child
五、事件对象(Event Object)
每个事件回调函数都会接收到一个事件对象(event
),它包含了许多有用的信息:
js
element.addEventListener('click', function(event) {
console.log(event.type); // 事件类型,如 'click'
console.log(event.target); // 触发事件的原始元素
console.log(event.currentTarget); // 当前监听事件的元素(this)
console.log(event.clientX, event.clientY); // 鼠标坐标
});
常用属性
属性 | 描述 |
---|---|
type |
事件类型 |
target |
触发事件的真实元素 |
currentTarget |
绑定监听器的元素(即 this ) |
bubbles |
该事件是否会冒泡 |
cancelable |
是否可以阻止默认行为 |
六、阻止默认行为与事件传播
1. 阻止默认行为:preventDefault()
适用于链接跳转、表单提交等默认行为。
js
form.addEventListener('submit', function(event) {
event.preventDefault(); // 阻止表单提交
console.log('表单未提交');
});
2. 阻止事件传播:stopPropagation()
阻止事件继续向上冒泡或向下捕获。
js
child.addEventListener('click', function(event) {
event.stopPropagation();
console.log('子元素被点击');
});
此时父元素的点击事件将不会被触发。
七、事件委托(Event Delegation)
由于事件会冒泡,我们可以将事件监听器放在父元素 上,统一处理子元素的事件,这就是事件委托。
html
<ul id="menu">
<li>首页</li>
<li>关于我们</li>
<li>联系我们</li>
</ul>
js
document.getElementById('menu').addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
console.log('你点击了:', event.target.innerText);
}
});
✅ 优点:
- 减少监听器数量
- 动态添加的子元素也能响应事件
- 提高性能
八、常见事件类型
事件类型 | 触发时机 | 事件类型 | 触发时机 |
---|---|---|---|
click |
鼠标点击或触屏点击 | change |
input 元素失去焦点后内容改变 |
mousedown/mouseup |
鼠标按下/释放 | submit |
表单提交 |
mousemove |
鼠标移动 | scroll |
页面或元素滚动 |
keydown/keyup |
键盘按键按下/释放 | resize |
窗口大小变化 |
input |
input 元素内容变化(实时) | load |
页面或资源加载完成 |
DOMContentLoaded |
HTML 文档解析完成但资源未加载完 |