为何JavaScript循环中用数组索引[i]无法播放音频,固定索引却可行?
循环绑定点击事件时音频路径失效的原因及解决方法
问题本质
你遇到的报错是因为**var声明的变量没有块级作用域**:循环执行时,所有事件监听器里的i都指向同一个全局变量。当循环结束后,i的值已经等于drum.length(也就是7),而你的playlist数组只有7个元素(索引范围0-6),playlist[7]是undefined,所以audio.src被设为无效值,导致音频加载失败。
而写死playlist[1]时,路径是有效的,所以能正常播放。
解决方法
方法1:用let声明循环变量i
let有块级作用域,每次循环都会创建一个独立的i变量,每个事件监听器引用的是当前循环的i值:
var audio = new Audio(); var playlist = ["sounds/crash.mp3","sounds/kick-bass.mp3","sounds/snare.mp3","sounds/tom-1.mp3","sounds/tom-2.mp3","sounds/tom-3.mp3","sounds/tom-4.mp3"]; var drum = document.querySelectorAll(".drum") // 把var改成let for (let i = 0; i < drum.length; i++) { drum[i].addEventListener("click", function play() { audio.src = playlist[i]; audio.play(); }); }
方法2:用立即执行函数创建闭包
通过立即执行函数(IIFE)把当前循环的i值保存到函数作用域中,避免所有监听器共享同一个变量:
var audio = new Audio(); var playlist = ["sounds/crash.mp3","sounds/kick-bass.mp3","sounds/snare.mp3","sounds/tom-1.mp3","sounds/tom-2.mp3","sounds/tom-3.mp3","sounds/tom-4.mp3"]; var drum = document.querySelectorAll(".drum") for (var i = 0; i < drum.length; i++) { // 用IIFE捕获当前i值 (function(index) { drum[index].addEventListener("click", function play() { audio.src = playlist[index]; audio.play(); }); })(i); }
方法3:使用forEach循环
forEach的回调函数会形成独立作用域,每个迭代的索引都是独立的:
var audio = new Audio(); var playlist = ["sounds/crash.mp3","sounds/kick-bass.mp3","sounds/snare.mp3","sounds/tom-1.mp3","sounds/tom-2.mp3","sounds/tom-3.mp3","sounds/tom-4.mp3"]; var drum = document.querySelectorAll(".drum") drum.forEach(function(element, index) { element.addEventListener("click", function() { audio.src = playlist[index]; audio.play(); }); });
方法4:利用元素自定义属性存储索引
把索引存在按钮的dataset属性里,点击时从元素上获取对应索引:
var audio = new Audio(); var playlist = ["sounds/crash.mp3","sounds/kick-bass.mp3","sounds/snare.mp3","sounds/tom-1.mp3","sounds/tom-2.mp3","sounds/tom-3.mp3","sounds/tom-4.mp3"]; var drum = document.querySelectorAll(".drum") for (var i = 0; i < drum.length; i++) { drum[i].dataset.index = i; // 给按钮存储对应索引 drum[i].addEventListener("click", function play() { var index = this.dataset.index; // 获取当前按钮的索引 audio.src = playlist[index]; audio.play(); }); }
内容的提问来源于stack exchange,提问作者Tuyen Nguyen
相关产品推荐
相关产品推荐

