监听音频播放结束事件应使用ended事件,但需确保音频自然播完、加载就绪后绑定、避免重复监听,并注意iOS需用户手势触发播放及loop属性影响。

监听音频播放结束事件,关键不是“加个事件就行”,而是确保音频真播完了、事件能正常触发、回调逻辑不被干扰。最常用也最可靠的方式是用 ended 事件,但它只在音频自然走到末尾时才触发——不是暂停、出错、中断,也不是循环重播时。
必须等音频加载就绪再监听
很多监听没反应,是因为绑得太早。audio 元素的 ended 事件依赖元数据(比如时长)已加载完成,否则浏览器可能根本不知道“末尾”在哪。
- 推荐监听
loadedmetadata或canplaythrough事件,等它触发后再绑定ended - 不要在
DOMContentLoaded就绑,此时src可能还没解析或加载 - 动态设置
src后,必须手动调用audio.load(),否则旧监听失效,新音频播完也不触发
正确绑定 ended 回调的方法
写法看似简单,但容易埋坑。直接写 onended="..." 或反复 addEventListener 都可能引发冲突或重复执行。
- 用
addEventListener('ended', handler, { once: true }),避免复用 audio 元素时重复绑定 - 如果要多次播放不同音频,换
src前先removeEventListener('ended', handler) - 回调里别只写日志,可做跳转下一曲、重置 UI、显示按钮、提交统计等实际动作
移动端特别是 iOS 的特殊限制
iOS Safari 对音频控制更严格:没有用户手势(比如点击按钮),play() 就不会真正开始,那 ended 自然也不会触发——因为压根没播起来。
立即学习“前端免费学习笔记(深入)”;
- 首次
play()必须由 click/tap 等用户操作触发 - 即使写了
audio.play().catch(...),iOS 也可能静默失败,readyState停在 0 或 1 - 别把关键流程(比如解锁下一关)完全依赖
ended,最好配合play()返回的 Promise 状态判断是否真启动了
常见导致 ended 不触发的原因
调试时发现回调没执行,大概率不是代码写错了,而是播放过程被意外打断。
- 音频根本没播起来:
readyState === 0(没加载任何元数据),可能是src为空、404 或被静音策略阻止 - 手动调用了
pause()或用户点了暂停——这触发的是pause事件,不是ended - 设置了
loop="true",循环模式下ended永远不会触发 - MP3 文件末尾帧损坏,某些浏览器(尤其是 iOS Safari)会提前终止,不发
ended



















