- 教程
- 示例工程
【免费下载链接】learning-area
GitHub repo for the MDN Learning Area.
导读
本指南围绕 MDN Learning Area 中「JavaScript 构建模块 · 事件(Events)」一章的配套练习展开,对应仓库文件为 marking.md。该练习通过三个由浅入深的任务,检验学习者对addEventListener()、键盘事件对象KeyboardEvent.key、事件冒泡(bubbling)与e.target的掌握程度。读完本文,你将得到每道任务的完整参考代码、逐行原理说明、可交互的在线练习入口,以及一套可复用的自评与调试方法,可直接对照本仓库中的起始代码进行练习与自查。
一、任务体系概览:三种练习形态
本仓库在 events 目录下为每个任务提供了两种文件形态:
| 文件 | 用途 |
|---|---|
eventsN.html(如 events1.html) | 可交互练习版:页面内置<textarea class="playable playable-js">代码编辑区与 Reset 按钮,输入代码实时执行 |
eventsN-download.html(如 events1-download.html) | 可下载的独立 HTML 版本,适合在浏览器中直接打开调试,错误会输出到浏览器 JavaScript 控制台 |
可交互版本的运行机制由同目录下的 playable.js 实现:页面加载时调用fillCode(),将编辑区中的代码通过eval()执行;用户每次输入都会触发input事件重新执行;若代码抛错,错误信息会被捕获并渲染到结果面板(section)中,帮助你定位问题。点击 Reset 按钮则恢复初始代码(playable.js)。页面的预览区与编辑区样式由 styles.css 统一定制。
也就是说:在页面上做练习时,如果代码有误,错误信息会直接显示在结果面板上;使用-download.html版本时,请打开浏览器开发者工具的 Console 查看报错。
二、Task 1:用addEventListener实现按钮状态切换
2.1 题目要求
为页面中的按钮创建一个简单的事件处理器:点击一次切换按钮文字,再点击一次切换回来。要求只修改 JavaScript,不改动 HTML。起始 HTML 结构(见 events1-download.html):
<button class="off">Machine is off</button>起始脚本:
const btn = document.querySelector('.off'); // Add your code here2.2 参考实现
const btn = document.querySelector("button"); btn.addEventListener("click", () => { if (btn.className === "on") { btn.textContent = "Machine is off"; btn.className = "off"; } else { btn.textContent = "Machine is on"; btn.className = "on"; } });2.3 逐行原理
document.querySelector("button"):选中页面中唯一的按钮元素,也可以沿用起始代码的document.querySelector('.off'),两者等价;btn.addEventListener("click", () => { ... }):为按钮挂载点击事件监听器。注意这里传入的是箭头函数形式的回调函数,而不是调用结果,因此函数体不会在注册时立即执行;- 状态判断:通过
btn.className判断当前按钮处于"on"还是"off"状态。初始 class 为"off",因此首次点击进入else分支,将文字改为"Machine is on"、class 改为"on";再次点击则走if分支,恢复"Machine is off"/"off"。
评分要点:该任务的核心是理解「事件监听器是函数的注册」以及「利用已有 DOM 属性维护状态」,参考实现中 class 切换同时服务于样式与状态判断,这是常见且简洁的做法。
三、Task 2:用键盘事件与KeyboardEvent.key驱动 Canvas 圆形移动
3.1 题目要求
为给定的 canvas 构建事件处理器,当按下 WASD 键时让圆形在画布内移动。圆形由预置函数drawCircle()绘制。起始代码(见 events2-download.html):
<canvas width="480" height="320" tabindex="0"></canvas>const canvas = document.querySelector('canvas'); const ctx = canvas.getContext('2d'); function drawCircle(x, y, size) { ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.beginPath(); ctx.fillStyle = 'black'; ctx.arc(x, y, size, 0, 2 * Math.PI); ctx.fill(); } let x = 50; let y = 50; const size = 30; drawCircle(x, y, size); // Add your code here3.2 参考实现
window.addEventListener("keydown", (e) => { switch (e.key) { case "a": x -= 2; break; case "d": x += 2; break; case "w": y -= 2; break; case "s": y += 2; break; } drawCircle(x, y, size); });3.3 逐行原理
window.addEventListener("keydown", ...):键盘事件监听器挂在window上,确保无论焦点在页面哪个位置都能捕获按键;e.key:事件对象KeyboardEvent的key属性返回按键的字符值——小写"a"、"d"、"w"、"s"。这是现代浏览器推荐的判断方式(相较已废弃的keyCode更直观、跨平台一致);switch分支:a让 x 坐标减 2(向左)、d让 x 加 2(向右)、w让 y 减 2(向上)、s让 y 加 2(向下);每个分支后的break必不可少,否则会发生 case 穿透(fall-through),导致一次按键同时执行多个位移;- 位移后调用
drawCircle(x, y, size)重绘。drawCircle内部先用白色fillRect清空整块画布,再以黑色arc + fill在新坐标绘制圆形,形成「移动 + 重绘」的经典 Canvas 动画模式。
评分要点:该任务考察键盘事件与事件对象的使用,以及状态变量(x/y)在事件回调间共享的能力。canvas上设置了tabindex="0",说明画布本身也可聚焦,键盘事件处理因此对可访问性同样友好。
四、Task 3:借助事件冒泡与e.target实现按钮组统一监听
4.1 题目要求
在按钮组的父元素上设置点击监听器:点击任意按钮时,利用e.target读取被点击按钮的data-color属性,并将.button-bar的背景色设为该颜色。要求不遍历所有按钮、不给每个按钮单独挂监听器,以此考查对事件冒泡(事件委托)的理解。起始结构(见 events3-download.html):
<div class="button-bar"> <button>const buttonBar = document.querySelector(".button-bar"); function setColor(e) { buttonBar.style.backgroundColor = e.target.getAttribute("data-color"); } buttonBar.addEventListener("click", setColor);4.3 逐行原理
buttonBar.addEventListener("click", setColor):监听器只挂在父容器.button-bar上。由于点击事件会从目标元素**向上冒泡(bubble)**到祖先元素,因此任意按钮的点击最终都会被父容器捕获,无需为 4 个按钮逐一注册监听器;e.target:事件对象上的target属性指向真正被点击的元素(即具体的某个<button>),而不是监听器所在的父容器,这是事件委托得以成立的关键;e.target.getAttribute("data-color"):通过getAttribute读取按钮上的自定义数据属性data-color的值("red"、"yellow"、"green"、"purple"),再赋给buttonBar.style.backgroundColor,完成背景色切换。
评分要点:这里尤其值得注意e.target与e.currentTarget的区别——前者是被点击的实际目标,后者是挂载监听器的元素。若误用e.currentTarget(即buttonBar),将因父容器没有data-color属性而取到null,背景色不会变化。这一陷阱正是该任务考察的核心知识点。
五、评估与调试:错误定位与自测清单
根据 marking.md 的说明,练习环境内置了错误反馈机制:
- 交互版(
eventsN.html):代码出错时,异常会被fillCode()捕获并渲染到结果面板(<section class="preview">),无需打开控制台即可看到错误信息; - 下载版(
eventsN-download.html):错误输出到浏览器 JavaScript 控制台,请按 F12 打开开发者工具查看; - Reset 按钮:任何时刻点击 Reset 即可将编辑区恢复为起始代码,便于反复重做。
自测清单可对照以下几点逐条验收:
- Task 1:初始文字为 "Machine is off",点击后变为 "Machine is on",再次点击恢复;按钮 class 在
on/off间往返切换; - Task 2:按下
a/d/w/s时圆形分别向左/右/上/下移动,且每次按键画布先被清空再重绘,无残影;一次按键只触发一个方向移动(验证break没有遗漏); - Task 3:分别点击四个按钮,背景色依次变为红、黄、绿、紫;整个实现只有一处
addEventListener(验证未给每个按钮单独绑定)。
六、结语
三个任务共同勾勒出 JavaScript 事件处理的完整能力图谱:addEventListener的正确注册(Task 1)、事件对象e.key的读取与状态驱动(Task 2)、事件冒泡与e.target的事件委托(Task 3)。结合本仓库的 可交互练习页(及 Task 2、Task 3)边改边跑,再对照 marking.md 的参考实现逐行比对,即可完成一次闭环的自我评估。如需扩展练习,还可以基于 events 目录下的起始文件,尝试为 Task 2 增加边界判断(防止圆形移出画布),或为 Task 3 增加事件委托之外的更多自定义属性读取逻辑。
- 教程
- 示例工程
【免费下载链接】learning-area
GitHub repo for the MDN Learning Area.
相关推荐
如何正确初始化transcribe参数结构体:transcribe_*_init函数使用规范
如何正确初始化transcribe参数结构体:transcribe_ _init函数使用规范 transcribe.cpp 是一个 C/C++ 语音转文本(sp
人工智能语音音频本地部署推理引擎Jetpack Compose 界面开发指南:Wanandroid 项目 UI 实现详解
Jetpack Compose 界面开发指南:Wanandroid 项目 UI 实现详解 Jetpack Compose 是 Android 官方推出的现代化
如何用115Exporter实现115网盘全速下载:终极完整指南
如何用115Exporter实现115网盘全速下载:终极完整指南 你是否厌倦了115网盘官方客户端的限速烦恼?想要彻底摆脱下载速度的束缚,体验真正的满带宽下载体
前端开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考