ended事件仅在音频自然播放完毕时触发,即播放指针到达末尾;不会因暂停、错误、中断、loop启用或未加载完成而触发,且ios需用户手势才能启动播放以确保ended可触发。

audio 元素的 ended 事件到底什么时候触发?
它只在音频**自然播放完**(即播放指针走到末尾)时触发,不是暂停、出错、被 pause() 或 load() 中断时触发。很多人误以为“播放停止了就触发”,结果回调一直不执行——其实是手动调用了 pause(),或者音频加载失败根本没播起来。
-
ended不会因网络中断、error事件、abort或用户点击暂停而触发 - 如果音频设置了
loop="true",ended永远不会触发(循环会重置播放位置) - 动态设置
src后必须显式调用load(),否则旧的监听可能失效,新音频播完也不触发ended
给 audio 绑定 ended 回调的正确写法
别在 HTML 里用 onended="handleEnd()",容易和后续 JS 逻辑冲突;也别用 addEventListener('ended', ...) 后不存引用,导致无法移除。最稳妥的是在元素就绪后绑定,并确保监听器在生命周期内有效。
- 先确认
audio元素已插入 DOM,且src已设置并完成加载(可监听canplaythrough后再绑) - 使用
addEventListener('ended', handler, { once: true })避免重复绑定(尤其在重用 audio 元素时) - 如果要复用同一
audio播不同文件,每次换src前用removeEventListener('ended', handler)清理旧监听
const audio = document.getElementById('my-audio');
const handleEnd = () => console.log('播放完了');
audio.addEventListener('ended', handleEnd, { once: true });
ended 事件不触发的三个高频原因
调试时发现回调没进,八成是以下之一:音频根本没播、播了但被干预、或监听时机错了。
-
audio.readyState === 0(HAVE_NOTHING):还没加载任何元数据,play()被静音策略阻止,或src是空字符串/404 - 调用了
audio.pause()或用户手动暂停,此时触发的是pause事件,不是ended - 监听代码写在
audio.src = 'xxx.mp3'之前,或写在DOMContentLoaded之前但元素尚未创建
移动端 iOS Safari 的特殊限制
iOS 上自动播放受限更严,play() 必须由用户手势触发(如 click/tap),否则 ended 根本没机会触发——因为压根播不起来。
立即学习“前端免费学习笔记(深入)”;
- 即使你写了
audio.play().catch(e => console.error(e)),iOS 也可能静默失败,readyState停在0或1 - 不要依赖
ended做关键流程(比如解锁下一关),得配合play()的 Promise 状态判断是否真开始了 - 测试务必真机,模拟器对音频策略模拟不准
ended 就容易变成“幽灵事件”——看不见、摸不着、查不到为什么没触发。核心就一条:它只认“自然走完”,其他全是干扰项。











