DOM 事件与高级操作
进阶 JavaScript DOM Events一、事件监听器(Event Listeners)
1.1 添加事件监听器
javascript
// 选择元素 → 添加监听器 → 指定回调
document.querySelector("button").addEventListener("click", handleClick);
function handleClick() {
alert("I got clicked!");
}addEventListener 接收两个参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| 第一个 | String | 事件类型(如 "click"、"keypress") |
| 第二个 | Function | 事件触发时执行的 回调函数 |
关键陷阱:不要加括号!
javascript
// ✅ 传递函数引用(等事件触发时再调用)
btn.addEventListener("click", handleClick);
// ❌ 立即调用函数(添加监听器时就执行了)
btn.addEventListener("click", handleClick());加括号 = 立即执行。不加括号 = 传递引用,等待调用。
1.2 匿名函数写法
javascript
document.querySelector("button").addEventListener("click", function () {
alert("I got clicked!");
});1.3 为多个元素添加监听器
用 for 循环 遍历所有目标元素:
javascript
var numberOfDrumButtons = document.querySelectorAll(".drum").length;
for (var i = 0; i < numberOfDrumButtons; i++) {
document.querySelectorAll(".drum")[i].addEventListener("click", function () {
// this → 触发事件的按钮
console.log(this.innerHTML);
});
}1.4 this 关键字
在事件监听器的回调函数中,this 指向 触发事件的元素:
javascript
document.querySelector("button").addEventListener("click", function () {
this.style.color = "white"; // 改变被点击按钮的文字颜色
console.log(this.innerHTML); // 获取按钮的文本内容
});二、高阶函数(Higher Order Functions)
2.1 概念
高阶函数 = 能接收其他函数作为参数的函数。
javascript
// add 和 multiply 是普通函数
function add(num1, num2) {
return num1 + num2;
}
function multiply(num1, num2) {
return num1 * num2;
}
// calculator 是高阶函数 — 接收函数作为参数
function calculator(num1, num2, operator) {
return operator(num1, num2);
}
calculator(4, 5, add); // 9
calculator(4, 5, multiply); // 202.2 为什么 addEventListener 是高阶函数
javascript
// addEventListener 接收一个函数作为第二个参数
element.addEventListener("click", respondToClick);
// ↑ 函数作为参数传入2.3 Chrome Debugger 调试
使用 debugger 关键字可以逐步跟踪函数调用:
javascript
debugger;
calculator(3, 4, multiply);在 Chrome DevTools 中点击 Step Into(↓) 可以逐行查看:
- 进入
calculator函数 num1 = 3,num2 = 4,operator = multiply- 调用
operator(3, 4)→ 进入multiply - 返回
12
三、回调函数(Callbacks)
3.1 概念
回调函数 = 作为参数传入另一个函数、等待某个事件发生后被 回调(call back) 执行的函数。
javascript
// respondToKey 是回调函数
document.addEventListener("keypress", respondToKey);
function respondToKey(event) {
console.log("Key pressed: " + event.key);
}执行流程:
addEventListener被调用,将respondToKey注册为回调- 浏览器持续监听键盘事件
- 用户按下按键 → 浏览器创建 事件对象
- 浏览器 回调
respondToKey,并传入事件对象
3.2 事件对象(Event Object)
回调函数可以接收一个 事件对象 参数:
javascript
document.addEventListener("keypress", function (event) {
console.log(event); // KeyboardEvent 对象
console.log(event.key); // "w"、"a" 等具体按键
console.log(event.type); // "keypress"
});💡 参数名可以是
event、e、evt——命名不影响功能,都指向同一个事件对象。
3.3 回调的底层原理
javascript
// addEventListener 的简化内部逻辑(伪代码)
function addEventListener(typeOfEvent, callback) {
// 1. 等待事件发生
// 2. 事件发生后,创建事件对象
var eventThatHappened = {
eventType: "keypress",
key: "p",
duration: 2
};
// 3. 检查事件类型是否匹配
if (eventThatHappened.eventType === typeOfEvent) {
// 4. 调用回调函数,传入事件对象
callback(eventThatHappened);
}
}理解回调的关键
回调函数 不是我们调用的,而是浏览器(或 addEventListener)在事件发生后调用的。
我们只是 注册 回调,告诉浏览器:"当事件发生时,帮我调用这个函数"。
四、键盘事件监听
4.1 监听键盘按键
把事件监听器添加到 整个文档(而非某个元素):
javascript
document.addEventListener("keypress", function (event) {
console.log(event.key); // 输出被按下的键
});4.2 Drum Kit 中的实际应用
javascript
// 按钮点击 → 获取 innerHTML
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", function () {
var buttonInnerHTML = this.innerHTML;
makeSound(buttonInnerHTML);
});
}
// 键盘按键 → 获取 event.key
document.addEventListener("keypress", function (event) {
makeSound(event.key);
});
// 统一的声音处理函数
function makeSound(key) {
switch (key) {
case "w":
var tom1 = new Audio("sounds/tom-1.mp3");
tom1.play();
break;
case "a":
var tom2 = new Audio("sounds/tom-2.mp3");
tom2.play();
break;
// ... 更多 case
default:
console.log(key);
}
}五、播放音频
5.1 创建和播放音频
javascript
var sound = new Audio("sounds/tom-1.mp3");
sound.play();5.2 Audio 构造函数
new Audio() 创建一个 HTMLAudioElement 对象:
javascript
// 等价于在 HTML 中写 <audio src="...">
var tom1 = new Audio("sounds/tom-1.mp3");Audio 对象的属性与方法:
| 属性/方法 | 类型 | 说明 |
|---|---|---|
src | 属性 | 音频文件路径 |
currentTime | 属性 | 当前播放位置 |
paused | 属性 | 是否暂停 |
play() | 方法 | 开始播放 |
pause() | 方法 | 暂停播放 |
六、Switch 语句
6.1 语法
适用于 多分支判断 的场景(比多个 if-else 更清晰):
javascript
switch (expression) {
case value1:
// 匹配 value1 时执行
break; // ← 必须 break,否则会"穿透"到下一个 case
case value2:
// 匹配 value2 时执行
break;
default:
// 都不匹配时执行(类似 else)
}6.2 实战示例
javascript
switch (key) {
case "w":
new Audio("sounds/tom-1.mp3").play();
break;
case "a":
new Audio("sounds/tom-2.mp3").play();
break;
case "s":
new Audio("sounds/tom-3.mp3").play();
break;
default:
console.log("Unknown key: " + key);
}switch vs if-else
- switch:适用于一个变量 等于 多个固定值的情况
- if-else:适用于复杂条件、范围判断、多变量组合
七、JavaScript 对象与构造函数
7.1 对象字面量
javascript
var bellBoy1 = {
name: "Timmy",
age: 19,
hasWorkPermit: true,
languages: ["French", "English"]
};
// 访问属性
bellBoy1.name; // "Timmy"
bellBoy1.languages; // ["French", "English"]7.2 构造函数(Object Constructor)
当需要批量创建相同结构的对象时,使用 构造函数(工厂模式):
javascript
// 构造函数名首字母大写
function BellBoy(name, age, hasWorkPermit, languages) {
this.name = name;
this.age = age;
this.hasWorkPermit = hasWorkPermit;
this.languages = languages;
}
// 用 new 关键字创建对象
var bellBoy1 = new BellBoy("Timmy", 19, true, ["French", "English"]);
var bellBoy2 = new BellBoy("Jimmy", 22, false, ["Spanish"]);构造函数的特征:
- 函数名 首字母大写(
BellBoy而非bellBoy) - 使用
this.属性 = 参数绑定属性 - 用
new关键字调用
7.3 对象方法
javascript
function BellBoy(name, age, hasWorkPermit, languages) {
this.name = name;
this.age = age;
this.hasWorkPermit = hasWorkPermit;
this.languages = languages;
// 方法 = 关联到对象的函数
this.moveSuitcase = function () {
alert("May I take your suitcase?");
// ... 提取行李逻辑
};
}
var bellBoy1 = new BellBoy("Timmy", 19, true, ["French"]);
bellBoy1.moveSuitcase(); // 调用对象方法7.4 理解 new Audio() 的本质
javascript
var tom1 = new Audio("sounds/tom-1.mp3");
tom1.play();底层原理(伪代码):
javascript
function Audio(fileLocation) {
this.fileLocation = fileLocation;
this.play = function () {
// 1. 访问音频硬件
// 2. 检查文件是否存在
// 3. 验证文件格式
// 4. 播放音频
};
}💡
new Audio(...)就是用 Audio 构造函数创建一个音频对象,.play()是该对象的方法。
八、动画效果
8.1 添加按钮按下效果
css
/* styles.css */
.pressed {
box-shadow: 0 3px 4px 0 #dbedf3;
opacity: 0.6;
}javascript
function buttonAnimation(currentKey) {
// 1. 找到对应按钮(通过类名)
var activeButton = document.querySelector("." + currentKey);
// 2. 添加 pressed 类
activeButton.classList.add("pressed");
// 3. 100ms 后移除(制造闪烁效果)
setTimeout(function () {
activeButton.classList.remove("pressed");
}, 100);
}8.2 setTimeout()
javascript
setTimeout(function, delay);| 参数 | 说明 |
|---|---|
function | 延迟后要执行的函数 |
delay | 等待时间(毫秒),1000ms = 1秒 |
javascript
// 3 秒后弹出 "Hello"
setTimeout(function () {
alert("Hello");
}, 3000);九、Dicee Game 项目实战 🎲
9.1 项目概述
一个双人骰子游戏:刷新页面 → 随机生成两个骰子 → 显示赢家。
9.2 核心逻辑
javascript
// 1. 生成随机数 (1-6)
var randomNumber1 = Math.floor(Math.random() * 6) + 1;
var randomNumber2 = Math.floor(Math.random() * 6) + 1;
// 2. 拼接图片路径
var randomImageSource1 = "images/dice" + randomNumber1 + ".png";
var randomImageSource2 = "images/dice" + randomNumber2 + ".png";
// 3. 修改图片 src 属性
document.querySelectorAll("img")[0].setAttribute("src", randomImageSource1);
document.querySelectorAll("img")[1].setAttribute("src", randomImageSource2);
// 4. 判断赢家
if (randomNumber1 > randomNumber2) {
document.querySelector("h1").innerHTML = "🚩 Player 1 Wins!";
} else if (randomNumber2 > randomNumber1) {
document.querySelector("h1").innerHTML = "Player 2 Wins! 🚩";
} else {
document.querySelector("h1").innerHTML = "Draw!";
}9.3 涉及的知识点
| 知识点 | 在项目中的应用 |
|---|---|
| 随机数 | Math.floor(Math.random() * 6) + 1 |
| 字符串拼接 | "images/dice" + num + ".png" |
| DOM 选择 | querySelectorAll("img")[0] |
| 属性操作 | .setAttribute("src", ...) |
| 条件语句 | if/else if/else 判断赢家 |
| innerHTML | 动态修改标题文本 |
十、Drum Kit 项目架构总结 🥁
index.html
├── <button class="drum w">w</button> ← 7 个按钮
├── ...
└── <script src="index.js"></script>
index.js
├── 为所有 .drum 按钮添加点击事件监听器
├── 为整个 document 添加键盘事件监听器
├── makeSound(key) ← switch 语句匹配按键 → 播放对应音频
└── buttonAnimation(key) ← classList.add("pressed") + setTimeout 移除核心代码结构:
javascript
// 按钮点击
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", function () {
var key = this.innerHTML;
makeSound(key);
buttonAnimation(key);
});
}
// 键盘按键
document.addEventListener("keypress", function (event) {
makeSound(event.key);
buttonAnimation(event.key);
});