拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Web-Dev-For-Beginners 打字游戏教程:基于 addEventListener 的浏览器事件驱动编程完整实践

Web-Dev-For-Beginners 打字游戏教程基于 addEventListener 的浏览器事件驱动编程完整实践【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners本文以 Web-Dev-For-Beginners 课程中的「使用事件建立游戏」章节为主体完整讲解事件驱动编程Event-Driven Programming的核心机制并带你从零实现一个会实时响应每一次按键的打字速度游戏理解事件监听者如何通过addEventListener注册与触发掌握click、input等常见浏览器事件的处理方式最终得到一个可用index.html、script.js、style.css三个文件搭建、可独立运行并可继续扩展的实战项目。一、为什么浏览器程序必须事件驱动当你为浏览器构建应用时你提供的是一个图形用户界面GUI用户在按钮、文本框等对象上点击或输入。这里有一个程序式编程无法回避的问题开发者无法预知用户何时会对这些对象产生互动——用户可能立刻点击开始按钮也可能五分钟后才动手甚至永远不会点击。事件驱动编程正是解决这一问题的范式。拆开这个名词主轴是事件Event即将要发生的事。它有效解决了上述不确定性我们知道当用户产生互动时哪些代码必须做出回应唯一不知道的是用户何时互动。回顾程序式编程函数依照顺序一行一行地被运行。事件驱动编程同样由函数组成差别在于如何去呼叫这些函数——不是按书写顺序执行而是等待某个事件发生后再执行。事件监听者的注册方式要处理点击按钮、输入文字这类事件需要注册事件监听者Event Listeners。事件监听者是一种函数负责在事件触发时提供相应回应更新用户界面、呼叫伺服器或做任何你希望它做的事。最通用的注册 API 是addEventListener它接收两个参数事件名称与要执行的函数。值得注意建立事件监听者有许多方式使用匿名函数arrow function或有名字的命名函数使用快捷方式如直接设定element.onclick handler这类事件属性使用addEventListener事件名以参数传入因此对任何事件都适用是弹性最高的方案。本课程聚焦addEventListener加匿名函数这一最常见、弹性最高的组合。常见事件浏览器提供了数十种可监听的事件——基本上用户在网页上做的任何行为都会触发事件你只需要处理其中一小部分。本课程游戏用到的核心事件是click与input其他常见事件包括事件触发时机典型用途click使用者點擊物件通常是按鈕或連結開始遊戲、提交表單contextmenu使用者點擊滑鼠右鍵自訂右鍵選單select使用者標記特定文字文字編輯、複製操作input使用者在輸入框中輸入文字即時驗證、即時搜尋二、游戏设计目标与流程游戏本身测试玩家的打字技巧——一项常被程序员忽视却极其重要的技能。大致流程如下玩家点击「开始」按钮游戏随机产生一行引文本游戏使用夏洛克·福尔摩斯的经典语录玩家尽快地将文字输入文字框当单词输入完毕时立即高亮标记下一个单词当玩家打错字时文字框转为红色当玩家完成整段引文输入时显示祝贺语与花费的时间。文件结构整个游戏只需三个文件index.html页面结构、script.js游戏逻辑、style.css视觉样式。这是驱动绝大多数网页的经典三件套。建立项目目录并进入# Linux 或 macOS mkdir typing-game cd typing-game # Windows md typing-game cd typing-game用 Visual Studio Code 打开当前目录code .然后在编辑器中新增三个文件index.html、script.js、style.css。三、建立使用者介面HTML回顾需求HTML 页面上需要四个食材呈现待输入引文的位置、呈现状态讯息如祝贺文的位置、玩家输入用的文字框、以及一个开始按钮。每个物件都需要唯一的id让 JavaScript 能控制它们同时在 HTML 中引入 CSS 与 JavaScript 文件。在index.html中加入以下代码!-- inside index.html -- html head titleTyping game/title link relstylesheet hrefstyle.css /head body h1Typing game!/h1 pPractice your typing skills with a quote from Sherlock Holmes. Click **start** to begin!/p p idquote/p !-- This will display our quote -- p idmessage/p !-- This will display any status messages -- div input typetext aria-labelcurrent word idtyped-value / !-- The textbox for typing -- button typebutton idstartStart/button !-- To start the game -- /div script srcscript.js/script /body /html结构要点#quote与#message是两个占位段落内容由 JavaScript 动态填充#typed-value输入框带有aria-labelcurrent word无障碍标签供屏幕阅读器识别其用途#start按钮是唯一的交互触发点script放在/body前确保脚本执行时 DOM 元素已经存在。执行应用程序最好的开发模式是定期确认程式结果。Visual Studio Code 有一个常用扩充套件 Live Server它会在你储存网页档案时同时架设本地服务器并自动刷新浏览器安装 Live Server 扩充套件后重啟 Visual Studio Code按下Ctrl-Shift-PmacOS 为Cmd-Shift-P開啟指令視窗輸入Live Server: Open with Live Server它会架設并发布你的网页開啟瀏覽器前往https://localhost:5500即可看到刚建立的页面。四、加入 CSS两种状态样式游戏需要两件事在视觉上一目了然玩家需要输入哪个单词、以及是否输错了。用两组 class 即可完成在style.css中加入/* 在 style.css 中 */ .highlight { background-color: yellow; } .error { background-color: lightcoral; border: red; }.highlight黄色背景标记当前要输入的单词引导玩家视线.error珊瑚红背景加红色边框打错字时立即反馈且不打断输入流。处理 CSS 时可以按自己的喜好规划版面更换字型、改变标题颜色、调整物件大小都不会影响后续 JavaScript 逻辑因为脚本只依赖id与这两个 class 名。仓库中的 范例解答样式 在同样两个 class 的基础上补充了全局重置、聚焦光晕与响应式布局可以作为美化参考。五、JavaScript游戏逻辑的实现5.1 建立常數同样以食材清单的方式程式需要储存所有引文的阵列陣列储存单一引文所有单词的空阵列追踪玩家当前正在输入单词的索引变数记录玩家点击开始时点的变数以及使用者介面三个元素的连结文字框typed-value、引文显示quote、讯息栏message。// 在檔案 script.js 中 // 所有的引文內容 const quotes [ When you have eliminated the impossible, whatever remains, however improbable, must be the truth., There is nothing more deceptive than an obvious fact., I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation., I never make exceptions. An exception disproves the rule., What one man can invent another can discover., Nothing clears up a case so much as stating it to another person., Education never ends, Watson. It is a series of lessons, with the greatest for the last., ]; // 儲存單字列表及目前要輸入的單字索引 let words []; let wordIndex 0; // 開始時間 let startTime Date.now(); // 網頁物件連結 const quoteElement document.getElementById(quote); const messageElement document.getElementById(message); const typedValueElement document.getElementById(typed-value);关键取舍引文清单在游戏过程中不会变化用constwords、wordIndex、startTime会随每局游戏重置用let。至于 DOM 元素虽然随时可以用document.getElementById取得但把它们存为常量可以避免字符串拼写错误、提高可读性也让后续引用一目了然。✅ 建议顺手往quotes里多加几句自己喜爱的引文。5.2 事件监听者一开始逻辑玩家点击start按钮时我们需要挑选一段引文、设定使用者介面、追踪当前单词与时间。由于不知道玩家何时点击所以必须用事件监听者。把以下代码追加到script.js末端// 在 script.js 末端 document.getElementById(start).addEventListener(click, () { // 取得一行引文 const quoteIndex Math.floor(Math.random() * quotes.length); const quote quotes[quoteIndex]; // 將引文分成許多單字存在矩陣中 words quote.split( ); // 重制單字索引來做追蹤 wordIndex 0; // 更新使用者介面 // 建立 span 元素的矩陣設定 class 用。 const spanWords words.map(function(word) { return span${word} /span}); // 轉換成字串並以 innerHTML 顯示引文 quoteElement.innerHTML spanWords.join(); // 標記第一個單字 quoteElement.childNodes[0].className highlight; // 清除訊息欄之前的訊息 messageElement.innerText ; // 設定文字框 // 清除文字框 typedValueElement.value ; // 設定 focus typedValueElement.focus(); // 設定事件驅動程式 // 開始計時器 startTime new Date().getTime(); });逐段拆解单字追踪设定Math.floor(Math.random() * quotes.length)Math.random()产生[0, 1)的随机小数乘以引文数量再向下取整得到合法的随机下标实现随机挑一行引文quote.split( )把引文按空格切成单词阵列存入words后续用它逐词比对wordIndex 0玩家总是从第一个单词开始输入。使用者介面设定words.map(...)生成spanWords每个单词被span.../span包裹这是后面能逐个单词切换 class 的前提spanWords.join()拼成一个字符串后写入quoteElement.innerHTML一次 DOM 更新即呈现整段引文quoteElement.childNodes[0].className highlight高亮第一个单词messageElement.innerText 清除上一局的祝贺/状态讯息。文字框与计时清空typedValueElement.value再调用typedValueElement.focus()让玩家点击开始后无需再点一次输入框就能直接打字startTime new Date().getTime()记下当前毫秒时间戳作为计算用时的基准。5.3 事件监听者二打字逻辑玩家每次按键input事件都会被触发。对应的监听者需要检查玩家是否输对了单词、监控整体游戏状况。追加到script.js最末端// script.js 最末端 typedValueElement.addEventListener(input, () { // 取得目前的單字 const currentWord words[wordIndex]; // 取得目前輸入的數值 const typedValue typedValueElement.value; if (typedValue currentWord wordIndex words.length - 1) { // 句子最末端 // 顯示成功 const elapsedTime new Date().getTime() - startTime; const message CONGRATULATIONS! You finished in ${elapsedTime / 1000} seconds.; messageElement.innerText message; } else if (typedValue.endsWith( ) typedValue.trim() currentWord) { // 單字最末端 // 清除輸入的數值準備給新的單字使用 typedValueElement.value ; // 移動到下一個單字 wordIndex; // 重設所有引文子元素的 class 名稱 for (const wordElement of quoteElement.childNodes) { wordElement.className ; } // 標記新單字 quoteElement.childNodes[wordIndex].className highlight; } else if (currentWord.startsWith(typedValue)) { // 單字目前輸入正確 // 標記下一個單字 typedValueElement.className ; } else { // 單字輸入錯誤 typedValueElement.className error; } });这段代码是一个从最具体到最一般的级联判断waterfall四个分支各管一种状态分支一整段引文完成。条件是typedValue currentWord且wordIndex words.length - 1——注意必须同时满足两个条件否则中间任何一个单词打对都会被误判为整句完成。完成后用当前时间减去startTime得到毫秒级用时除以 1000 转为秒拼进祝贺讯息写入messageElement。分支二当前单词完成。以空格为界typedValue.endsWith( )且trim()后与currentWord相等说明玩家刚打完一个单词并按了空格。此时清空输入框、wordIndex推进到下一词先把quoteElement所有子元素的className重置为恢复默认呈现再把新词的 class 设为highlight。分支三输入正确但未完成。currentWord.startsWith(typedValue)为真表示已输入部分是目标单词的前缀此时清除className让输入框回到默认样式从错误红恢复。分支四输入错误。以上皆不成立说明已经打错把className设为error触发珊瑚红背景——反馈即时且不打断输入。六、测试你的应用程序点击start按钮、开始输入单词效果应与上文开头的演示动画一致。测试清单随机引文是否正常显示、当前单词高亮是否随输入推进、打错字时输入框是否变红、打完单词后是否自动清空并前进、整句完成后是否显示耗时祝贺文。遇到错误是常态——所有的开发者都会面对错误查看程式讯息并侦错本身就是练习的一部分。常用排查手段按 F12 打开浏览器 DevTools 的 Console 查看 JavaScript 报错、确认三个文件名与引入路径完全一致区分大小写、确认 Live Server 正在运行并正确刷新。源码级印证仓库 solution 版本的工程化改进仓库提供了一个经过重构的解答实现 solution/index.js对照学习可以看出课程代码向生产级写法演进的典型方向命名函数取代匿名函数开始逻辑与打字逻辑被抽成startGame与handleTyping两个命名函数注册时只需startButton.addEventListener(click, startGame)见 index.js 第90-91行可读性更好也便于后续移除/重新注册监听者这类挑战querySelector取代getElementByIdDOM 引用统一为document.querySelector(#quote)等写法见 第20-23行classList取代直接赋值className高亮切换收敛为一个highlightWord工具函数用el.classList.toggle(highlight, i index)一行完成重置所有 高亮当前见 第44-48行错误状态则用classList.add(error)/classList.remove(error)避免了直接覆盖className可能误伤其它 class 的问题完成即禁用输入框游戏结束时执行typedValueElement.disabled true见 第69-73行直接实现了课程挑战中完成引文时关闭文字框这一条集中管理文案祝贺、错误、开始提示收敛为messages物件见 第26-30行成功讯息用seconds.toFixed(2)保留两位小数比课程版的整数秒更精细。配套的 solution/index.html 还演示了更完整的无障碍做法用label fortyped-value classhidden-label配合视觉隐藏样式替代单纯的aria-labelsolution/index.css 则把.highlight/.error两个核心 class 保留不变在其外围补充了聚焦态、阴影与响应式断点——验证了逻辑只依赖 id 与 class 名这一设计的好处样式可以任意重做而不触碰脚本。七、挑战继续扩展游戏课程列出的进阶挑战solution 版本已替你实现了其中一部分可对照源码学习实现方式在完成游戏时关闭input事件监听者removeEventListener重新开始时再开启——这需要把监听函数存为具名引用才能移除当玩家完成引文时关闭文字框设定disabled以**对话窗格dialog/modal**的方式显示祝贺讯息利用localStorage储存最高分用setItem/getItem持久化最佳成绩刷新页面后依然可读。此外英文原版课程还布置了一个 AI Agent 挑战为游戏实现难度系统——追踪 WPM 与正确率、按表现自动切换 Easy/Medium/Hard 三档引文池、在 UI 上显示当前难度与统计、连击 3 次后升档并配以颜色/动画反馈。八、复习与自學浏览浏览器为开发者提供的所有事件类型文档逐一思考每种事件适用的实际场景——你只会用到其中一小撮但要能说出为什么用input而不用change这类取舍完成课程附带的课前/课后测验巩固事件驱动的概念模型本课的正式作业是 建立一款新的鍵盤遊戲基于键盘事件开发一款小游戏评分标准以呈现完整的遊戲內容優良/ 遊戲內容單調普通/ 遊戲出現問題待改進三档衡量。小结本教程完整走通了GUI 不确定性 → 事件监听者 →click启动游戏 →input实时判定 → 状态样式反馈 → 计时结算的事件驱动闭环。addEventListener加匿名函数、split/startsWith做逐词校验、className切换做视觉反馈、时间戳差值做计时这四个模式几乎可以直接迁移到你日后写的任何交互式网页。相关文档可回溯至 繁體中文課程原文、英文課程原文 与 章節總覽。【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门