我实际上一直在解决这个问题。它给了我这个错误“未捕获的TypeError:无法读取未定义的属性'play'”。当我使用foreach循环时,一切都很好,但是使用普通的for循环给我这个错误,我不知道为什么。
这是使用javascript的代码
window.addEventListener("load", function () {
const pads = document.querySelectorAll(".pads div");
const sounds = document.querySelectorAll(".sound");
var numberOfPads = document.querySelectorAll(".pads div").length;
for (var i = 0; i < numberOfPads; i++) {
pads[i].addEventListener("click", function () {
sounds[i].play();
});
}
});
HTML代码:
<body>
<section class="app">
<header>
<h1>Beatmaker</h1>
</header>
<div class="visual">
<div class="pads">
<div class="pad1">
<audio class="sound" src="sounds/bubbles.mp3">
</audio>
</div>
<div class="pad2">
<audio class="sound" src="sounds/clay.mp3">
</audio>
</div>
<div class="pad3">
<audio class="sound" src="sounds/confetti.mp3">
</audio>
</div>
</div>
<div class="pads">
<div class="pad4">
<audio class="sound" src="sounds/glimmer.mp3">
</audio>
</div>
<div class="pad5">
<audio class="sound" src="sounds/moon.mp3">
</audio>
</div>
<div class="pad6">
<audio class="sound" src="sounds/ufo.mp3">
</audio>
</div>
</div>
</div>
</section>
</body>
答案 0 :(得分:0)
这是一个棘手的问题。发生这种情况的原因是 i
是函数作用域的,这意味着一个相同的i
用于函数中的任何点。这也适用于将来的事情。循环修改 i
,完成循环后,i
将比最大值高1。
让我们按照发生的顺序逐步进行此操作。为了清楚起见:我看到了6个div,因此我们有i
的有效值,范围是0到5(包括)。
load
事件处理程序执行:
i
为0。因为0小于6,所以循环体运行。pads[i].addEventListener("click", ...)
与i == 0
一起执行i
为1。因为1小于6,所以循环体运行。pads[i].addEventListener("click", ...)
与i == 1
一起执行i
是5。因为5小于6,所以循环体运行。pads[i].addEventListener("click", ...)
与i == 5
一起执行i
是6。您的循环正文不运行,因为6不小于6。pad4
。
click
处理程序运行(i
为3时设置的处理程序)。i
,所以 i
现在是6 (请参阅之前的最后一步!)。sounds[i].play()
运行,试图访问sounds[6]
sounds[6]
未定义,因此您无法读取其属性play
并收到错误消息。它与forEach
一起使用的原因是它使用了回调,这意味着您在其中访问的i
是回调函数的参数,该参数对于具体功能调用。因此,您基本上每次都会获得一个新的i
,这在将来也将保持其价值(它永远不会被修改)。这样,您基本上得到了6个不同的i
,而不是1个随时间变化的i
!
如果您想继续使用for
循环,则必须使用let
而不是var
:
for (let i = 0; i < numberOfPads; i++) {
// ...
}
这是因为let
创建了一个块作用域变量。我知道现在它的工作方式有些混乱,因为i
仍在修改中,但这是由于for
与let
结合使用时有特殊的便利性:实际上在此处创建两个i
,一个每次都被修改(您在for (...)
内部看到的一个),另一个在每次迭代时从修改后的副本创建为副本(您在内部看到的一个)循环体)。
如果对此感到困惑,另一种(更合乎逻辑)解决问题的方法是手动将i
分配给循环内的块作用域副本:
window.addEventListener("load", function () {
const pads = document.querySelectorAll(".pads div");
const sounds = document.querySelectorAll(".sound");
var numberOfPads = document.querySelectorAll(".pads div").length;
for (var i = 0; i < numberOfPads; i++) {
const j = i; // Create local copy
pads[j].addEventListener("click", function () {
sounds[j].play(); // Now we use j here and not i
});
}
});
(我用const
来强调它不会被修改,甚至也不能被修改,但是let
也是可以的。var
不会!)