Day 11: 事件处理
前情回顾: 在 Day 10 中,我们学习了 DOM 操作基础,了解了如何选择、修改、创建和删除 DOM 元素。今天我们要学习的是如何让页面"活"起来——响应用户的操作。
🎯 学习目标
- 理解什么是事件和事件流
- 掌握事件监听器的添加和移除
- 了解事件对象(Event Object)的属性和方法
- 学会阻止默认行为和事件冒泡
- 掌握常见的事件类型(鼠标、键盘、表单等)
- 了解事件委托的原理和应用
💡 核心概念
什么是事件(Event)?
事件是文档或浏览器窗口中发生的特定交互瞬间。例如:
- 用户点击按钮
- 用户按下键盘
- 页面加载完成
- 表单提交
事件是 JavaScript 与用户交互的核心机制。
事件流
当一个事件发生时,事件会在 DOM 树中传播。有三个阶段:
- 捕获阶段(Capture Phase): 从
window向下传播到目标元素 - 目标阶段(Target Phase): 事件到达目标元素
- 冒泡阶段(Bubbling Phase): 从目标元素向上传播回
window
点击按钮时的流程:
捕获阶段:window → document → html → body → div → button
目标阶段:button
冒泡阶段:button → div → body → html → document → window
默认情况下,事件监听器在冒泡阶段触发。
📝 基本语法
添加事件监听器
使用 addEventListener() 方法添加事件监听器:
element.addEventListener(event, function, useCapture);
参数:
event: 事件名称(如"click","mouseover")function: 事件处理函数useCapture: 可选,是否在捕获阶段触发(默认false)
示例:
// 选择按钮
const button = document.querySelector('button');
// 添加点击事件
button.addEventListener('click', function(event) {
console.log('按钮被点击了!');
});
移除事件监听器
使用 removeEventListener() 方法移除:
// 定义命名函数
function handleClick(event) {
console.log('按钮被点击了!');
}
// 添加监听器
button.addEventListener('click', handleClick);
// 移除监听器
button.removeEventListener('click', handleClick);
注意: 只能移除命名函数,不能移除匿名函数!
🎮 示例代码
示例 1: 基本的点击事件
<!DOCTYPE html>
<html>
<head>
<title>事件处理示例</title>
</head>
<body>
<button id="myButton">点击我</button>
<p id="message">等待点击...</p>
<script>
// 选择元素
const button = document.getElementById('myButton');
const message = document.getElementById('message');
// 添加点击事件
button.addEventListener('click', function() {
message.textContent = '按钮被点击了!';
message.style.color = 'green';
});
</script>
</body>
</html>
效果: 点击按钮后,文字会变为绿色并显示"按钮被点击了!"
示例 2: 事件对象(Event Object)
事件处理函数会接收一个事件对象,包含事件的详细信息:
button.addEventListener('click', function(event) {
// event.type: 事件类型
console.log('事件类型:', event.type); // "click"
// event.target: 触发事件的元素
console.log('目标元素:', event.target); // <button>元素
// event.clientX, event.clientY: 鼠标位置
console.log('鼠标位置:', event.clientX, event.clientY);
// event.timeStamp: 事件发生的时间戳
console.log('时间戳:', event.timeStamp);
});
示例 3: 阻止默认行为
有些元素有默认行为,例如:
- 点击链接会跳转
- 点击提交按钮会提交表单
- 按空格键会向下滚动
使用 event.preventDefault() 可以阻止默认行为:
<a id="link" href="https://example.com">点击我不会跳转</a>
<script>
const link = document.getElementById('link');
link.addEventListener('click', function(event) {
// 阻止链接跳转
event.preventDefault();
alert('链接被点击了,但没有跳转!');
});
</script>
示例 4: 阻止事件冒泡
<div id="outer" style="padding: 50px; background: lightblue;">
外层
<div id="inner" style="padding: 30px; background: lightcoral;">
内层
</div>
</div>
<script>
const outer = document.getElementById('outer');
const inner = document.getElementById('inner');
outer.addEventListener('click', function() {
console.log('外层被点击');
});
inner.addEventListener('click', function(event) {
console.log('内层被点击');
// 阻止事件冒泡到外层
event.stopPropagation();
});
</script>
效果: 点击内层时,只会输出"内层被点击",不会触发外层的点击事件。
示例 5: 常见事件类型
鼠标事件
const box = document.getElementById('box');
// 点击
box.addEventListener('click', function() {
console.log('点击');
});
// 双击
box.addEventListener('dblclick', function() {
console.log('双击');
});
// 鼠标移入
box.addEventListener('mouseenter', function() {
this.style.backgroundColor = 'lightblue';
});
// 鼠标移出
box.addEventListener('mouseleave', function() {
this.style.backgroundColor = 'lightcoral';
});
// 鼠标移动
box.addEventListener('mousemove', function(event) {
console.log(`鼠标位置: ${event.clientX}, ${event.clientY}`);
});
键盘事件
// 按下按键
document.addEventListener('keydown', function(event) {
console.log(`按键: ${event.key}`);
console.log(`键码: ${event.code}`);
// 检测特定按键
if (event.key === 'Enter') {
console.log('回车键被按下');
}
// 检测组合键
if (event.ctrlKey && event.key === 's') {
event.preventDefault(); // 阻止保存
console.log('Ctrl+S 被按下');
}
});
// 释放按键
document.addEventListener('keyup', function(event) {
console.log(`按键 ${event.key} 被释放`);
});
表单事件
<form id="myForm">
<input type="text" id="username" placeholder="用户名">
<input type="email" id="email" placeholder="邮箱">
<button type="submit">提交</button>
</form>
<script>
const form = document.getElementById('myForm');
const username = document.getElementById('username');
// 表单提交
form.addEventListener('submit', function(event) {
event.preventDefault(); // 阻止表单默认提交
const name = username.value;
console.log('提交的用户名:', name);
});
// 输入框内容变化
username.addEventListener('input', function(event) {
console.log('当前输入:', event.target.value);
});
// 获得焦点
username.addEventListener('focus', function() {
this.style.backgroundColor = 'lightyellow';
});
// 失去焦点
username.addEventListener('blur', function() {
this.style.backgroundColor = 'white';
console.log('最终输入:', this.value);
});
</script>
页面事件
// 页面加载完成
window.addEventListener('load', function() {
console.log('页面所有资源(图片、样式等)已加载完成');
});
// DOM 加载完成(更快)
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM 已加载完成,可以操作元素了');
});
// 页面滚动
window.addEventListener('scroll', function() {
console.log('滚动位置:', window.scrollY);
});
// 窗口大小改变
window.addEventListener('resize', function() {
console.log('窗口大小:', window.innerWidth, window.innerHeight);
});
示例 6: 事件委托
事件委托是利用事件冒泡机制,将事件处理交给父元素处理。
问题: 如果有 100 个按钮,是否要添加 100 个监听器?
解决: 使用事件委托,只添加 1 个监听器!
<ul id="list">
<li><button>按钮 1</button></li>
<li><button>按钮 2</button></li>
<li><button>按钮 3</button></li>
<!-- 可能有更多按钮 -->
</ul>
<script>
const list = document.getElementById('list');
// 只添加一个监听器到父元素
list.addEventListener('click', function(event) {
// 检查点击的是否是按钮
if (event.target.tagName === 'BUTTON') {
console.log('点击了:', event.target.textContent);
}
});
// 动态添加的按钮也会生效!
const li = document.createElement('li');
const button = document.createElement('button');
button.textContent = '按钮 4';
li.appendChild(button);
list.appendChild(li);
</script>
事件委托的优势:
- ✅ 减少事件监听器数量(提高性能)
- ✅ 动态添加的元素自动绑定事件
- ✅ 代码更简洁
✍️ 练习任务
练习 1: 点击计数器
目标: 创建一个计数器,每次点击按钮就加 1。
要求:
- 显示当前计数值(初始为 0)
- 点击"增加"按钮,计数 +1
- 点击"减少"按钮,计数 -1
- 点击"重置"按钮,计数归零
HTML 结构:
<h1>计数器: <span id="count">0</span></h1>
<button id="increase">增加</button>
<button id="decrease">减少</button>
<button id="reset">重置</button>
提示:
- 使用
addEventListener为每个按钮添加事件 - 使用
textContent更新计数值
练习 2: 输入验证
目标: 创建一个表单,实时验证用户输入。
需求:
- 用户名:至少 3 个字符
- 邮箱:必须包含
@ - 密码:至少 8 个字符
- 实时显示验证结果(✓ 或 ✗)
提示:
- 使用
input事件监听输入变化 - 使用正则表达式验证邮箱格式
练习 3: 简易待办事项
目标: 使用事件委托实现待办事项列表。
功能:
- 输入框输入任务,按回车添加
- 点击任务可以切换完成状态
- 点击删除按钮可以删除任务
- 使用事件委托处理点击事件
提示:
- 使用
keydown事件检测回车键 - 使用
event.target判断点击的是哪个元素 - 使用
element.classList.toggle()切换样式
练习 4: 键盘快捷键
目标: 实现一个支持快捷键的简单应用。
快捷键:
Ctrl + B: 加粗选中文字Ctrl + I: 斜体选中文字Ctrl + U: 下划线选中文字Escape: 清除格式
提示:
- 使用
document.execCommand()实现文本格式化 - 使用
event.ctrlKey检测 Ctrl 键
🎓 今日挑战
交互式画板
目标: 创建一个简单的画板应用。
功能需求:
-
画图功能:
- 鼠标按下开始画图
- 鼠标移动时绘制线条
- 鼠标松开停止画图
-
工具栏:
- 选择颜色
- 选择画笔粗细
- 清空画板
- 保存图片
-
快捷键:
Ctrl + Z: 撤销Ctrl + S: 保存C: 清空
技术要点:
- 使用
<canvas>元素 - 使用
mousedown,mousemove,mouseup事件 - 使用
canvas.getContext('2d')API - 使用
canvas.toDataURL()保存图片
难度: ⭐⭐⭐⭐☆
💡 常见问题
Q: addEventListener 和 onclick 有什么区别?
A: 推荐使用 addEventListener,原因:
- 可以添加多个监听器:
// onclick 只能有一个
button.onclick = function() { /* ... */ };
button.onclick = function() { /* ... */ }; // 覆盖上面的
// addEventListener 可以有多个
button.addEventListener('click', function() { /* ... */ });
button.addEventListener('click', function() { /* ... */ }); // 两个都执行
-
可以控制捕获/冒泡阶段
-
可以移除监听器
Q: 什么时候使用事件委托?
A: 在以下情况使用事件委托:
- 有大量相似元素(如列表、表格)
- 动态添加元素(新元素自动绑定事件)
- 提高性能(减少监听器数量)
Q: event.target 和 event.currentTarget 有什么区别?
A:
event.target: 触发事件的原始元素event.currentTarget: 绑定事件的当前元素
outer.addEventListener('click', function(event) {
console.log(event.target); // 点击的元素(可能是 inner)
console.log(event.currentTarget); // outer(绑定监听器的元素)
});
📌 关键要点
- ✅ 使用
addEventListener()添加事件监听器 - ✅ 事件处理函数会接收一个事件对象(
event) - ✅
event.preventDefault()阻止默认行为 - ✅
event.stopPropagation()阻止事件冒泡 - ✅ 常见事件类型:
click,keydown,input,submit,load - ✅ 事件委托利用冒泡机制,在父元素处理子元素事件
- ✅ 事件委托可以提高性能,适合动态元素
学习时间: 2026-03-11
难度: ⭐⭐⭐☆☆
预计用时: 2-3 小时
关键词: 事件, addEventListener, 事件对象, 事件委托
相关标签: #07-DOM操作
🎉 恭喜完成 Day 11! 你已经掌握了 JavaScript 事件处理的核心知识。下一课我们将学习表单处理和验证。