Skip to content

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);  // 20

2.2 为什么 addEventListener 是高阶函数

javascript
// addEventListener 接收一个函数作为第二个参数
element.addEventListener("click", respondToClick);
//                                 ↑ 函数作为参数传入

2.3 Chrome Debugger 调试

使用 debugger 关键字可以逐步跟踪函数调用:

javascript
debugger;
calculator(3, 4, multiply);

在 Chrome DevTools 中点击 Step Into(↓) 可以逐行查看:

  1. 进入 calculator 函数
  2. num1 = 3num2 = 4operator = multiply
  3. 调用 operator(3, 4) → 进入 multiply
  4. 返回 12

三、回调函数(Callbacks)

3.1 概念

回调函数 = 作为参数传入另一个函数、等待某个事件发生后被 回调(call back) 执行的函数。

javascript
// respondToKey 是回调函数
document.addEventListener("keypress", respondToKey);

function respondToKey(event) {
  console.log("Key pressed: " + event.key);
}

执行流程:

  1. addEventListener 被调用,将 respondToKey 注册为回调
  2. 浏览器持续监听键盘事件
  3. 用户按下按键 → 浏览器创建 事件对象
  4. 浏览器 回调 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"
});

💡 参数名可以是 eventeevt——命名不影响功能,都指向同一个事件对象。

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);
});

← 返回 Web 开发研究