JavaScript: JavaScript 事件
事件是用户或浏览器发出的"信号"——用户点了按钮、按了键盘、提交了表单,浏览器加载完页面……这些都是事件。你的代码"监听"这些事件,就能在恰当的时机做出响应。这就像门铃响了你去开门——门铃是事件,开门是响应。
1. 事件绑定方式
JavaScript 有三种给元素绑定事件处理函数的方式:
▶ 示例
JAVASCRIPT
// 本节代码示例
const message = "你好,世界!";
console.log(message);
这是最简单的示例,后续章节会详细解释。### (1) 1. HTML 属性(不推荐)
HTML
<button onclick="alert('别用这种方式')">点我</button>
HTML 和 JS 混在一起,难以维护,不推荐。
(2) 2. DOM 属性
HTML
<button id="myBtn">点我</button>
<script>
const btn = document.getElementById('myBtn');
btn.onclick = function() {
console.log('点击了');
};
</script>
HTML 和 JS 分离了,但一个事件只能绑一个处理函数——后赋值会覆盖前赋值。
(3) 3. addEventListener(推荐)
HTML
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<script>
const btn1 = document.getElementById('btn1');
const btn2 = document.getElementById('btn2');
btn1.addEventListener('click', function() {
console.log('处理函数1');
});
btn1.addEventListener('click', function() {
console.log('处理函数2');
});
</script>
同一个事件可以绑多个处理函数,还能用 removeEventListener 移除,是最灵活的方式。
💡 提示: 三种方式的取舍:忘掉前两种,始终用
addEventListener。它不会覆盖已有监听器,还能控制冒泡/捕获阶段,是现代开发的唯一选择。
2. addEventListener 与 removeEventListener
HTML
<script>
element.addEventListener('事件名', 处理函数, 选项);
element.removeEventListener('事件名', 处理函数);
</script>
⚠️ 注意:
removeEventListener 的处理函数必须和 addEventListener 传入的是同一个引用。匿名函数无法移除!需要移除时,必须把函数单独定义。
HTML
<button id="btn">测试</button>
<script>
const btn = document.getElementById('btn');
function handler() { console.log('只触发一次'); }
btn.addEventListener('click', handler);
btn.removeEventListener('click', handler); // 成功移除
btn.addEventListener('click', function() { console.log('匿名'); });
btn.removeEventListener('click', function() { console.log('匿名'); }); // 移除失败!
</script>
3. 常见事件类型
| 类别 | 事件 | 说明 |
|---|---|---|
| 鼠标 | click、dblclick |
单击、双击 |
| 鼠标 | mouseover、mouseout |
鼠标移入/移出 |
| 键盘 | keydown、keyup |
按键按下/松开 |
| 表单 | submit |
表单提交 |
| 表单 | change |
值改变并失焦后触发 |
| 表单 | input |
值实时改变(每输一个字符) |
| 页面 | load |
页面及资源全部加载完 |
| 页面 | DOMContentLoaded |
HTML 解析完即触发(不等图片) |
💡 提示:
change vs input:input 是打字就触发,change 是输入完失焦才触发。实时搜索用 input,表单验证用 change。
4. 事件对象 event
事件处理函数会收到一个事件对象参数,通常叫 event 或简写 e。
| 属性/方法 | 说明 |
|---|---|
e.target |
触发事件的元素(真正被点击的那个) |
e.type |
事件类型,如 'click' |
e.preventDefault() |
阻止默认行为(如阻止链接跳转、表单提交) |
e.stopPropagation() |
阻止事件冒泡 |
▶ 示例:按钮点击计数
HTML
<button id="counter">点击了 0 次</button>
<script>
const btn = document.getElementById('counter');
let count = 0;
btn.addEventListener('click', function(e) {
count++;
btn.textContent = '点击了 ' + count + ' 次';
console.log('事件类型:', e.type);
console.log('触发元素:', e.target.tagName);
});
</script>
▶ 示例:键盘检测
HTML
<p>按下任意键查看信息</p>
<div id="info" style="padding: 10px; border: 1px solid #ccc; min-height: 30px;"></div>
<script>
const info = document.getElementById('info');
document.addEventListener('keydown', function(e) {
info.innerHTML = '按键: <b>' + e.key + '</b> | 键码: ' + e.keyCode + ' | Ctrl: ' + e.ctrlKey + ' | Shift: ' + e.shiftKey;
});
</script>
▶ 示例:表单拦截
HTML
<form id="myForm">
<input type="text" id="name" placeholder="输入姓名" required>
<button type="submit">提交</button>
</form>
<p id="result"></p>
<script>
const form = document.getElementById('myForm');
const result = document.getElementById('result');
form.addEventListener('submit', function(e) {
e.preventDefault();
const name = document.getElementById('name').value;
result.textContent = '你好, ' + name + '! 表单已被拦截,没有真正提交。';
});
</script>
▶ 示例:事件委托
HTML
<ul id="menu">
<li>首页</li>
<li>关于</li>
<li>联系</li>
<li>帮助</li>
</ul>
<p id="selected"></p>
<script>
const menu = document.getElementById('menu');
const selected = document.getElementById('selected');
menu.addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
selected.textContent = '你选择了: ' + e.target.textContent;
}
});
</script>
💡 提示: 上面这个技巧叫"事件委托"——把监听器绑在父元素上,用
e.target 判断真正被点击的子元素。不用给每个 li 单独绑事件,新增子元素也自动生效。这就是冒泡机制的妙用。
5. 事件冒泡与捕获
DOM 事件传播分三个阶段:
- 捕获阶段:事件从
window一路向下传到目标元素 - 目标阶段:事件到达实际触发的元素
- 冒泡阶段:事件从目标元素一路向上冒泡回
window
默认情况下,addEventListener 在冒泡阶段触发。设第三个参数为 true 则在捕获阶段触发。
HTML
window
↓ 捕获
document
↓
body
↓
目标元素 ← 目标阶段
↑ 冒泡
body
↑
document
↑
window
💡 提示: 大多数情况下你不需要关心捕获,用默认的冒泡就够了。
e.stopPropagation() 可以阻止事件继续传播,但别滥用——它可能打断事件委托。
❓ 常见问题
Q
e.target 和 e.currentTarget 有什么区别?A
e.target 是真正触发事件的元素(你点的那一个),e.currentTarget 是当前正在执行处理函数的元素(绑监听器的那个)。在事件委托中两者不同,在直接绑定时两者相同。Q 为什么表单提交后页面刷新了?
A
<form> 的 submit 事件默认行为就是提交并刷新页面。调用 e.preventDefault() 可以阻止它,然后用 JS 处理数据。Q
onclick 和 addEventListener('click') 能同时用吗?A 能,但
onclick 赋值会覆盖之前通过 onclick 绑的函数,而 addEventListener 不会。混用容易混乱,统一用 addEventListener 最省心。📖 小节
- 三种绑定方式中,
addEventListener是唯一推荐的方式,支持多监听器、可移除、可控制传播阶段 removeEventListener必须传入同一个函数引用,匿名函数无法移除- 事件对象
e的核心属性:target(谁触发的)、type(什么事件)、preventDefault()(阻止默认行为)、stopPropagation()(阻止传播) changevsinput:前者失焦触发,后者实时触发- 事件冒泡使得"事件委托"成为可能——监听父元素,用
e.target处理子元素 DOMContentLoaded比load更早触发,适合初始化 DOM 操作
📝 作业
- 基础:创建一个按钮,用
addEventListener绑定click事件,每次点击让按钮的宽度增加 10px。 - 进阶:实现一个键盘乐器——监听
keydown事件,按 A/S/D/F/G 分别在页面上显示不同的音符名(Do/Re/Mi/Fa/Sol)。 - 挑战:用事件委托实现一个动态菜单——有一个
ul列表和"添加项"按钮,新增的li点击后也能高亮显示(不需要给新li单独绑事件)。 - 附加:实现一个
input事件实时搜索——输入框打字时,下方列表只显示包含输入文字的项。