JavaScript30鼓点项目点击事件报Cannot read 'innerText'错误如何解决
报错原因
你遇到的报错是JS经典的var变量作用域+异步回调闭包问题,具体原因如下:
index.js:66 Uncaught TypeError: Cannot read properties of undefined (reading 'innerText') at HTMLDivElement.
(index.js:66)
- 你的for循环用
var声明变量i,var不存在块级作用域,整个循环共享同一个i变量 - 点击事件是异步回调,只有当你点击元素时才会执行,此时for循环已经执行完毕,
i的值最终等于所有.key元素的总数量(你有9个按键,所以i=9) - 执行
document.getElementsByTagName("kbd")[i]时,kbd元素总共有9个,索引范围是0~8,取索引9自然返回undefined,读取undefined的innerText属性就抛出了错误 - 额外逻辑缺陷:即使i的问题修复,直接从全局取
kbd列表匹配索引的写法也不健壮,若页面其他地方存在kbd元素就会出现匹配错误
修复方案
有两种常用修复方式,推荐优先用第二种:
方案1:替换变量声明关键字
把for循环的var i改成let i,let具备块级作用域,每次循环都会生成独立的i绑定,回调函数能正确捕获当前循环的索引值:
// 仅修改for循环第一行即可 for (let i = 0; i < document.querySelectorAll(".key").length; ++i) { document.querySelectorAll(".key")[i].addEventListener("click", function() { var keyPress = document.getElementsByTagName("kbd")[i].innerText; playAudio(keyPress); buttonAnimation(keyPress); }); }
方案2:直接从当前点击元素取值(更推荐)
点击事件回调里的this指向当前被点击的.key元素,直接在当前元素下查找kbd即可,完全不需要依赖循环索引,稳定性更高:
for (var i = 0; i < document.querySelectorAll(".key").length; ++i) { document.querySelectorAll(".key")[i].addEventListener("click", function() { // 直接取当前点击元素内的kbd文本 var keyPress = this.querySelector('kbd').innerText; playAudio(keyPress); buttonAnimation(keyPress); }); }
额外优化提示
你当前的playAudio函数里所有case都引用了clap.wav,会导致所有按键播放同一个音效,按需求替换成对应的音效文件即可。
内容的提问来源于stack exchange,提问作者Sudhanshu Shekhar
相关产品推荐
相关产品推荐

