jQuery onended回调调用音频函数自动切歌出现无限重复卡顿问题求解
异常根因定位
- 事件重复叠加:你当前把
thissound.onended的逻辑写在.audio元素的点击回调内部,每次用户点击播放按钮,都会给同一个全局音频对象重新覆盖onended事件处理函数。多次点击后,旧的嵌套回调没有被完全清除,当音频自然播放结束时,多层叠加的回调会被依次触发,反复调用EvalSound重置播放状态,最终就会出现卡顿、无限重复同一段音频的问题。 - 闭包变量失效:代码里的
nextfile是点击回调的局部变量,依赖闭包引用给onended回调传值。每次新的点击触发后,这个局部变量的值会被重新赋值,导致之前绑定的回调引用的还是旧的变量值,切歌逻辑完全混乱,无法按顺序切换后续章节。实际运行中最典型的无限重复场景就是:切到第2章后,onended回调里引用的还是首次点击时保存的nextfile=2,第2章播完后又被触发播放第2章,陷入死循环。 - 状态判断缺失:当前在
onended回调中调用EvalSound(nextfile)时,没有校验下一个音频文件是否存在,也没有区分“手动暂停触发的状态变化”和“自然播放结束的状态变化”,容易触发无意义的音频加载、播放请求,造成性能卡顿。
可行修复方案
将事件绑定逻辑从点击回调中抽离,全局统一绑定一次音频结束事件,用全局状态管理自动切歌的目标,彻底避免事件叠加问题,修复后代码如下:
var thissound = new Audio(); var currentKey; // 全局存储自动切歌的目标编号,替代原局部闭包变量 var autoTargetKey; function EvalSound(key) { if (currentKey !== key) { // 切换新音频源前重置状态,避免旧音频残留的播放状态造成卡顿 thissound.pause(); thissound.src = "media/audio/chapters/" + chapternum + "/" + key + ".mp3"; thissound.currentTime = 0; // 每次切换新音频时,自动更新下一个要播放的相邻章节编号 autoTargetKey = parseInt(key) + 1; } currentKey = key; if (thissound.paused) { thissound.play(); } else { thissound.pause(); thissound.currentTime = 0; currentPlayer = thissound; } } // 全局仅绑定一次音频播放结束的事件,放在点击逻辑外部,避免重复绑定叠加回调 thissound.onended = function() { // 只有存在自动切歌目标时才执行切歌逻辑 if (autoTargetKey) { // 校验目标章节的DOM元素是否存在,避免请求不存在的音频文件报错 if ($(`li#${autoTargetKey}`).length) { EvalSound(autoTargetKey); } else { // 播放到最后一章时清空切歌标记,避免无效逻辑调用 autoTargetKey = null; } } }; $(".audio").click(function(e) { e.preventDefault(); // 从当前触发的按钮向上取章节编号,替代多层parent()的脆弱写法 var chapterId = $(this).closest('li.liste').attr('id'); // 直接调用播放函数即可,不需要额外设置结束回调 EvalSound(chapterId); });
修复核心点:
- 事件只绑定一次:彻底解决多次点击导致的
onended回调叠加、重复触发的问题 - 状态全局管理:用
autoTargetKey统一记录切歌目标,避免闭包变量被覆盖导致的逻辑错乱,从根源上避免单段音频被无限重复播放 - 增加边界校验:切歌前校验目标章节是否存在,避免加载不存在的音频文件造成的卡顿、报错
- 优化元素选取逻辑:用jQuery原生的
closest()方法替代多层parent()取元素的写法,减少DOM层级依赖,代码适配性更强
内容的提问来源于stack exchange,提问作者S.B.
相关产品推荐
相关产品推荐

