JavaScript Event 事件
Event 事件
1. 事件中的 Event 对象
Event 对象是事件触发时自动创建的核心对象,包含事件相关的所有状态信息,其核心特性如下:
- 自动创建:只要有事件发生(如点击、滚动、输入),浏览器就会自动生成一个 Event 对象。
- 核心作用:记录事件触发时的关键信息,例如:
- 事件发生的目标元素(哪个元素触发了事件);
- 鼠标位置、键盘按键状态(如 Ctrl 键是否按下);
- 事件类型(如
click、mouseover)。
- 执行时机:绑定的事件处理函数不会提前执行,仅在事件触发时,携带 Event 对象作为参数执行。
- 特殊注意:若在 HTML 标签内通过
onclick属性直接绑定事件(如<button onclick="handleClick()">),函数内需通过window.event才能获取 Event 对象,而非参数接收。
2. Event 对象常见属性与方法
| 类别 | 属性 / 方法 | 作用描述 |
|---|---|---|
| 目标相关 | target |
获取触发事件的元素(事件源,可能是绑定事件元素的子元素) |
currentTarget |
获取绑定事件的元素(与 this 效果一致,不受事件冒泡影响) |
|
| 事件信息 | type |
获取事件类型(如 click、scroll、keydown) |
clientX/clientY |
获取鼠标相对于浏览器可视区的坐标(不受页面滚动影响) | |
offsetX/offsetY |
获取鼠标相对于事件源元素(target)左上角的坐标 |
|
| 行为控制 | preventDefault() |
阻止事件的默认行为(如 a 标签跳转、表单提交) |
stopPropagation() |
阻止事件冒泡(阻止事件向父元素传递) | |
dataset |
获取元素上通过 data-* 定义的自定义属性(如 data-index → e.target.dataset.index) |
3. 阻止默认事件
部分 HTML 元素存在默认行为(如 a 标签跳转、表单提交、右键菜单弹出),若需自定义交互逻辑,需阻止这些默认行为,常用两种实现方式:
方式 1:return false(适用于 DOM0 级事件绑定)
当通过 on+事件名 或 attachEvent(IE 低版本)绑定事件时,通过 return false 阻止默认行为:
html
预览
<a id="link" href="https://www.baidu.com">百度链接</a>
<script>
var link = document.getElementById("link");
// DOM0 级绑定:onclick
link.onclick = function() {
alert("点击链接但不跳转");
return false; // 阻止 a 标签默认跳转行为
};
</script>
方式 2:event.preventDefault()(适用于 DOM2 级事件绑定)
当通过 addEventListener 绑定事件时,需调用 Event 对象的 preventDefault() 方法:
html
预览
<a id="link" href="https://www.baidu.com">百度链接</a>
<script>
var link = document.getElementById("link");
// DOM2 级绑定:addEventListener
link.addEventListener("click", function(event) {
// 兼容 IE 低版本:获取 Event 对象
var ev = event || window.event;
alert("点击链接但不跳转");
ev.preventDefault(); // 阻止默认跳转行为
});
</script>
兼容封装(支持所有浏览器)
为避免浏览器兼容性问题,可封装通用的阻止默认事件函数:
javascript
运行
/**
* 阻止事件的默认行为(兼容所有浏览器)
* @param {Event} event - 事件对象
*/
function preventDefault(event) {
var ev = event || window.event;
// 标准浏览器
if (ev.preventDefault) {
ev.preventDefault();
} else {
// IE 低版本(IE6-8)
ev.returnValue = false;
}
}
// 使用示例
link.addEventListener("click", function(event) {
preventDefault(event); // 直接调用封装函数
alert("阻止默认跳转");
});
4. 事件流:事件冒泡与事件捕获
事件流描述了事件在 DOM 树中传递的过程,分为 事件捕获、目标阶段、事件冒泡 三个阶段,主流浏览器均遵循 W3C 标准的 DOM 事件流。
4.1 事件冒泡(默认行为)
- 定义:事件从触发事件的元素(最具体的子元素)开始,逐级向上传递到 DOM 根节点(如
div → body → html → document → window),类似水泡从水底上升到水面。 - 浏览器差异:
- IE 6.0:冒泡到
document为止; - 其他浏览器(Chrome、Firefox、Edge):冒泡到
window。
- IE 6.0:冒泡到
- 注意:并非所有事件都支持冒泡,如
blur(失去焦点)、focus(获取焦点)、load(加载完成)、unload(卸载)等事件不冒泡。
4.2 事件冒泡示例
html
预览
<style>
#father {
width: 300px;
height: 300px;
background: red;
margin: 50px auto;
}
#btn {
margin: 100px;
padding: 10px 20px;
}
</style>
<div id="father">
<button id="btn">点击按钮</button>
</div>
<script>
window.onload = function() {
var father = document.getElementById("father");
var btn = document.getElementById("btn");
// 子元素:按钮点击事件
btn.onclick = function() {
alert("按钮被点击(子元素)");
};
// 父元素:div 点击事件
father.onclick = function() {
alert("div 被点击(父元素)");
};
// 根节点:document 点击事件
document.onclick = function() {
alert("文档被点击(根节点)");
};
};
</script>
- 执行结果:点击按钮时,会依次弹出
按钮被点击 → div 被点击 → 文档被点击,体现事件从子元素向父元素冒泡的过程。
4.3 阻止事件冒泡
若需避免事件传递到父元素,需阻止冒泡,两种实现方式:
- 标准浏览器:
event.stopPropagation(); - IE 低版本:
event.cancelBubble = true。
兼容封装与示例
javascript
运行
/**
* 阻止事件冒泡(兼容所有浏览器)
* @param {Event} event - 事件对象
*/
function stopPropagation(event) {
var ev = event || window.event;
// 标准浏览器
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
// IE 低版本
ev.cancelBubble = true;
}
}
// 使用示例(修改上述冒泡案例)
btn.onclick = function(event) {
stopPropagation(event); // 阻止冒泡
alert("按钮被点击(子元素)"); // 仅执行此弹窗,父元素事件不触发
};
4.4 事件捕获
- 定义:与事件冒泡相反,事件从 DOM 根节点(如
window)开始,逐级向下传递到触发事件的子元素,即 “从外到内”。 - 开启方式:通过
addEventListener的第三个参数useCapture控制,useCapture: true表示开启事件捕获,默认false(事件冒泡)。
事件捕获示例
html
预览
<style>
.body {
width: 100%;
height: 400px;
background: red;
}
.wrap {
width: 300px;
height: 300px;
background: green;
margin: 50px auto;
}
.inner {
width: 100px;
height: 100px;
background: blue;
margin: 100px auto;
}
</style>
<body class="body">
<div class="wrap">
<div class="inner"></div>
</div>
</body>
<script>
var body = document.querySelector(".body");
var wrap = document.querySelector(".wrap");
var inner = document.querySelector(".inner");
// 开启事件捕获(useCapture: true)
body.addEventListener("click", function() {
console.log("body 捕获阶段触发");
}, true);
wrap.addEventListener("click", function() {
console.log("wrap 捕获阶段触发");
}, true);
inner.addEventListener("click", function() {
console.log("inner 目标阶段触发");
}, true);
</script>
- 执行结果:点击蓝色
inner元素时,控制台输出顺序为body 捕获 → wrap 捕获 → inner 目标,体现 “从外到内” 的捕获过程。
5. 事件委托(事件代理)
事件委托是基于事件冒泡机制的优化方案,核心思想:将子元素的事件绑定到父元素上,通过父元素统一处理所有子元素的事件。
5.1 核心场景
当子元素动态生成(如购物车添加商品、列表动态加载)时,直接给子元素绑定事件会失效,此时需通过父元素代理事件。
5.2 事件委托示例(购物车删除商品)
html
预览
<style>
* {
margin: 0;
padding: 0;
list-style: none;
}
.show {
width: 400px;
margin: 50px auto;
}
button {
padding: 8px 16px;
cursor: pointer;
}
li {
height: 60px;
line-height: 60px;
border: 1px solid #ff0000;
margin: 10px 0;
padding: 0 20px;
cursor: pointer;
}
</style>
<div class="show">
<button id="addBtn">点击添加商品</button>
<p id="emptyTip">暂时没有商品</p>
<ul id="goodsList">
<li data-index="11">老商品 1(点击删除)</li>
<li data-index="12">老商品 2(点击删除)</li>
</ul>
</div>
<script>
var addBtn = document.getElementById("addBtn");
var emptyTip = document.getElementById("emptyTip");
var goodsList = document.getElementById("goodsList");
var goodsCount = 12; // 商品索引计数器
// 1. 初始化:判断是否显示“无商品”提示
function updateEmptyTip() {
var liList = goodsList.querySelectorAll("li");
emptyTip.style.display = liList.length === 0 ? "block" : "none";
}
updateEmptyTip();
// 2. 点击添加商品(动态生成子元素)
addBtn.onclick = function() {
goodsCount++;
var li = document.createElement("li");
li.setAttribute("data-index", goodsCount);
li.innerHTML = `新商品 ${goodsCount - 12}(点击删除)`;
goodsList.appendChild(li);
updateEmptyTip();
};
// 3. 事件委托:通过父元素 ul 处理子元素 li 的点击事件
goodsList.onclick = function(e) {
var ev = e || window.event;
var target = ev.target; // 获取触发事件的子元素(li)
// 判断是否点击的是 li 元素(避免误触发其他子元素)
if (target.tagName === "LI") {
var goodsIndex = target.dataset.index; // 获取商品索引
alert(`删除商品,索引:${goodsIndex}`);
goodsList.removeChild(target); // 删除当前 li
updateEmptyTip();
}
};
</script>
5.3 事件委托的优缺点
| 优点 | 缺点 |
|---|---|
| 减少事件注册数量,节省内存(无需给每个子元素绑定事件) | 依赖事件冒泡,不支持冒泡的事件(如 blur)无法使用 |
| 动态生成的子元素自动拥有事件(无需重新绑定) | 层级过深时,冒泡过程可能被中间层阻止(需避免随意阻止冒泡) |
| 简化 DOM 更新时的事件管理(删除子元素无需解绑事件,避免内存泄漏) | 需判断触发事件的目标元素,可能出现误判(需精准匹配标签 / 类名) |
| 推荐 “就近委托”(如 ul 代理 li),而非全局委托(如 document 代理所有元素) |
6. 事件相关工具函数封装
为提升代码复用性和兼容性,可封装事件绑定、解绑、阻止默认行为、阻止冒泡的通用函数:
javascript
运行
// 1. 绑定事件(兼容所有浏览器)
function addEvent(obj, eventType, handler) {
if (obj.addEventListener) {
// 标准浏览器(DOM2 级)
obj.addEventListener(eventType, handler);
} else if (obj.attachEvent) {
// IE 低版本(DOM2 级)
obj.attachEvent("on" + eventType, handler);
} else {
// 最原始方式(DOM0 级)
obj["on" + eventType] = handler;
}
}
// 2. 解绑事件(兼容所有浏览器)
function removeEvent(obj, eventType, handler) {
if (obj.removeEventListener) {
// 标准浏览器
obj.removeEventListener(eventType, handler);
} else if (obj.detachEvent) {
// IE 低版本
obj.detachEvent("on" + eventType, handler);
} else {
// 最原始方式
obj["on" + eventType] = null;
}
}
// 3. 阻止默认事件(已封装,见前文)
function preventDefault(event) {
var ev = event || window.event;
ev.preventDefault ? ev.preventDefault() : (ev.returnValue = false);
}
// 4. 阻止事件冒泡(已封装,见前文)
function stopPropagation(event) {
var ev = event || window.event;
ev.stopPropagation ? ev.stopPropagation() : (ev.cancelBubble = true);
}
// 使用示例
var btn = document.getElementById("btn");
function handleClick() {
alert("事件触发");
}
// 绑定事件
addEvent(btn, "click", handleClick);
// 解绑事件
removeEvent(btn, "click", handleClick);
7. e.target、this 与 e.currentTarget 的区别
三者的核心差异在于 “指向的元素不同”,具体区别如下:
| 对象 | 指向的元素 | 适用场景 |
|---|---|---|
e.target |
触发事件的元素(事件源) | 事件委托中判断子元素 |
this |
绑定事件的元素 | 普通事件绑定中操作当前元素 |
e.currentTarget |
绑定事件的元素(与 this 一致) |
需明确区分绑定元素时使用 |
示例对比
html
预览
<div class="box" style="width: 200px; height: 200px; background: yellow; margin: 50px auto;">
外层 box
<button style="margin: 80px;">内层按钮</button>
</div>
<ul class="list" style="width: 200px; margin: 50px auto;">
<li>列表项 1</li>
<li>列表项 2</li>
<li>列表项 3</li>
</ul>
<script>
// 案例 1:普通元素(无嵌套子元素)
var box = document.querySelector(".box");
box.onclick = function(e) {
console.log("this:", this); // 指向 box(绑定事件的元素)
console.log("e.target:", e.target); // 指向 box(触发事件的元素,无嵌套)
console.log("e.currentTarget:", e.currentTarget); // 指向 box(与 this 一致)
};
// 案例 2:嵌套元素(父元素绑定事件,子元素触发)
var list = document.querySelector(".list");
list.onclick = function(e) {
console.log("this:", this); // 指向 list(绑定事件的父元素)
console.log("e.target:", e.target); // 指向 li(触发事件的子元素)
console.log("e.currentTarget:", e.currentTarget); // 指向 list(与 this 一致)
};
核心区别总结
| 对象 | 指向的元素 | 典型应用场景 |
|---|---|---|
e.target |
触发事件的具体元素(事件源) | 事件委托中判断点击的子元素类型(如 li/button) |
this |
绑定事件的元素 | 普通事件处理中操作当前元素(如修改样式、属性) |
e.currentTarget |
绑定事件的元素(与 this 完全一致) |
需要明确区分绑定元素与触发元素时使用 |
实战场景对比
html
预览
<div class="card" style="border: 1px solid #ccc; padding: 20px; margin: 20px;">
<h3 class="title">商品标题</h3>
<p class="price">¥99.00</p>
<button class="buy-btn">加入购物车</button>
</div>
<script>
var card = document.querySelector('.card');
card.addEventListener('click', function(e) {
console.log('this:', this); // 始终指向 .card(绑定事件的元素)
console.log('e.currentTarget:', e.currentTarget); // 与 this 一致,指向 .card
console.log('e.target:', e.target); // 点击不同区域指向不同元素:
// 点击标题 → <h3>
// 点击价格 → <p>
// 点击按钮 → <button>
// 实际应用:根据 target 执行不同操作
if (e.target.classList.contains('buy-btn')) {
console.log('加入购物车');
} else if (e.target.classList.contains('title')) {
console.log('查看商品详情');
}
});
</script>
8. 常用事件类型详解
JavaScript 提供了丰富的事件类型,覆盖用户交互的各种场景,以下是开发中最常用的几类:
8.1 鼠标事件(Mouse Events)
处理鼠标相关操作,如点击、移动、悬停等。
| 事件名 | 触发时机 | 支持冒泡 |
|---|---|---|
click |
鼠标左键单击元素 | 是 |
dblclick |
鼠标左键双击元素 | 是 |
mousedown |
鼠标按下(任意键) | 是 |
mouseup |
鼠标抬起(任意键) | 是 |
mousemove |
鼠标在元素上移动 | 是 |
mouseover |
鼠标移入元素(含子元素) | 是 |
mouseout |
鼠标移出元素(含子元素) | 是 |
mouseenter |
鼠标移入元素(不含子元素,不冒泡) | 否 |
mouseleave |
鼠标移出元素(不含子元素,不冒泡) | 否 |
contextmenu |
鼠标右键点击(打开上下文菜单前) | 是 |
应用示例:鼠标悬停效果
html
预览
<style>
.box {
width: 200px;
height: 200px;
background-color: #f0f0f0;
transition: all 0.3s;
margin: 50px;
}
.box.active {
background-color: #4285f4;
transform: scale(1.1);
}
</style>
<div class="box" id="box"></div>
<script>
var box = document.getElementById('box');
// mouseenter 与 mouseleave(不冒泡,适合精确控制)
box.addEventListener('mouseenter', function() {
this.classList.add('active');
});
box.addEventListener('mouseleave', function() {
this.classList.remove('active');
});
// 对比:mouseover 与 mouseout(会冒泡,子元素也会触发)
// box.addEventListener('mouseover', function() { ... });
// box.addEventListener('mouseout', function() { ... });
</script>
8.2 键盘事件(Keyboard Events)
处理键盘输入,常用于表单验证、快捷键等场景。
| 事件名 | 触发时机 | 支持冒泡 |
|---|---|---|
keydown |
键盘按下(按住会持续触发) | 是 |
keyup |
键盘抬起 | 是 |
keypress |
按下字符键(已过时,推荐用 keydown) |
是 |
常用属性
key:获取按下的键(如a、Enter、ArrowUp)keyCode:获取键码(数字,如65代表a,已逐步被key替代)ctrlKey/shiftKey/altKey:布尔值,判断 Ctrl/Shift/Alt 键是否同时按下
应用示例:快捷键控制
html
预览
<input type="text" id="input" placeholder="按 Ctrl+S 保存">
<div id="status"></div>
<script>
var input = document.getElementById('input');
var status = document.getElementById('status');
document.addEventListener('keydown', function(e) {
// 检测 Ctrl+S 快捷键
if (e.ctrlKey && e.key === 's') {
e.preventDefault(); // 阻止浏览器默认保存行为
status.textContent = '已保存内容:' + input.value;
}
// 检测 Esc 键清空输入
if (e.key === 'Escape') {
input.value = '';
status.textContent = '已清空输入';
}
});
</script>
8.3 表单事件(Form Events)
处理表单元素的交互,如输入、提交、焦点变化等。
| 事件名 | 触发时机 | 支持冒泡 |
|---|---|---|
submit |
表单提交(点击提交按钮或按 Enter) | 是 |
reset |
表单重置 | 是 |
focus |
元素获取焦点(如输入框被点击) | 否 |
blur |
元素失去焦点(如输入框被点击后离开) | 否 |
input |
元素值发生变化(实时触发,如输入文字) | 是 |
change |
元素值发生变化且失去焦点(如下拉框选择) | 是 |
应用示例:表单验证
html
预览
<form id="loginForm">
<div>
<label>用户名:</label>
<input type="text" name="username" id="username">
<span class="error" id="userError"></span>
</div>
<div>
<label>密码:</label>
<input type="password" name="password" id="password">
<span class="error" id="pwdError"></span>
</div>
<button type="submit">登录</button>
</form>
<script>
var form = document.getElementById('loginForm');
var username = document.getElementById('username');
var password = document.getElementById('password');
var userError = document.getElementById('userError');
var pwdError = document.getElementById('pwdError');
// 实时验证用户名
username.addEventListener('input', function() {
if (this.value.length < 3) {
userError.textContent = '用户名至少3个字符';
userError.style.color = 'red';
} else {
userError.textContent = '√';
userError.style.color = 'green';
}
});
// 表单提交验证
form.addEventListener('submit', function(e) {
var isValid = true;
// 验证用户名
if (username.value.length < 3) {
userError.textContent = '用户名不能为空且至少3个字符';
isValid = false;
}
// 验证密码
if (password.value.length < 6) {
pwdError.textContent = '密码至少6个字符';
isValid = false;
}
if (!isValid) {
e.preventDefault(); // 验证失败,阻止表单提交
} else {
alert('提交成功!');
}
});
</script>
8.4 窗口事件(Window Events)
处理浏览器窗口相关操作,如加载、 resize、滚动等。
| 事件名 | 触发时机 | 支持冒泡 |
|---|---|---|
load |
页面所有资源(图片、脚本等)加载完成 | 否 |
DOMContentLoaded |
DOM 加载完成(无需等待图片等资源) | 否 |
resize |
窗口大小改变时 | 否 |
scroll |
页面或元素滚动时 | 是 |
beforeunload |
页面即将关闭或刷新前 | 否 |
应用示例:响应式布局与滚动监听
html
预览
<div id="scrollTop" style="position: fixed; bottom: 30px; right: 30px; width: 50px; height: 50px; background: #4285f4; color: white; border-radius: 50%; display: none; align-items: center; justify-content: center; cursor: pointer;">↑</div>
<script>
var scrollTopBtn = document.getElementById('scrollTop');
// 监听页面滚动
window.addEventListener('scroll', function() {
// 滚动距离超过 300px 显示回到顶部按钮
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
scrollTopBtn.style.display = scrollTop > 300 ? 'flex' : 'none';
});
// 点击回到顶部
scrollTopBtn.addEventListener('click', function() {
window.scrollTo({
top: 0,
behavior: 'smooth' // 平滑滚动
});
});
// 监听窗口大小变化
window.addEventListener('resize', function() {
console.log('窗口宽度:', window.innerWidth);
console.log('窗口高度:', window.innerHeight);
// 可根据窗口大小调整布局
});
</script>
更多推荐


所有评论(0)