ended事件是唯一可靠、跨浏览器的音频播放结束钩子,必须用JavaScript绑定,且play()需由用户手势触发,否则无法播放更不会触发ended。

用 标签插入音频并绑定 ended 事件
直接写 只能播放,监听不到结束。必须用 JavaScript 绑定 ended 事件——这是唯一可靠、跨浏览器的播放结束钩子。
注意:不要用 timeupdate 轮询判断 currentTime === duration,精度差、有误差、还浪费 CPU。
常见错误是把事件监听写在 document.addEventListener('DOMContentLoaded', ...) 里但没等 元素加载完成,导致 addEventListener 失败。更稳妥的做法是获取元素后立刻绑定,或确保 DOM 已就绪且 src 已设置。
-
ended事件只在自然播放完(非用户暂停/跳转)时触发 - 若音频被
load()后未调用play(),或中途调用pause(),不会触发ended - 动态设置
src后,需等loadeddata或canplay事件后再调用play(),否则 iOS Safari 可能静音失败
ended 事件监听的最小可行代码
以下代码可直接粘贴进 HTML 文件测试:
<audio id="myAudio" src="sound.mp3"></audio><script>
const audio = document.getElementById('myAudio');
audio.addEventListener('ended', () => {
console.log('音频播放完毕');
// 在这里执行后续逻辑,比如自动播放下一首、显示按钮、提交埋点
});
// 确保音频能播放(尤其移动端需要用户手势触发)
document.body.addEventListener('click', () => audio.play().catch(e => console.warn('autoplay prevented:', e)), { once: true });
</script>
关键点:ended 是标准事件名,大小写敏感;audio.play() 必须由用户手势(如 click/touchend)触发,否则 Chrome 和 Safari 会拒绝播放并抛出 NotAllowedError。
如果音频路径无效或格式不支持,ended 永远不会触发,而是触发 error 事件——建议同时监听它做兜底。
多个音频切换时 ended 监听容易漏掉的坑
当页面有多个 ,或频繁更换 src(比如播放列表),别重复绑定事件,也别忘记清理旧监听器。
典型错误是每次换歌都写 audio.addEventListener('ended', handler),导致同一事件触发多次 handler。
- 推荐做法:复用同一个
元素,用removeEventListener清理前一个 handler,或用箭头函数 + 变量保存引用以便移除 - 更简单方案:用事件委托不现实(
ended不冒泡),所以必须对每个实例单独管理 - 更换
src后,旧的ended监听依然有效,但不会触发——因为播放已中断;新音频需重新调用play()才可能触发新ended - iOS Safari 对
preload="none"的音频,在首次play()前不加载元数据,duration为NaN,但ended仍能正常触发
兼容性与静音策略下 ended 是否仍有效
只要音频实际开始播放过(哪怕只有 1ms),ended 就会在播放自然结束时触发,和是否静音、音量是否为 0 无关。
但要注意:Chrome 77+ 和 Safari 强制要求“用户手势启动媒体”,否则 play() 返回 Promise 并 reject,后续什么事件都不会发生——ended 自然也不会触发。
- 解决方法:所有
play()调用必须包裹在用户交互回调中(click、touchstart),且不能异步延迟(比如setTimeout(..., 100)会失效) - 后台标签页中,Chrome 可能节流音频解码,但不影响
ended触发时机 - 使用
audio.muted = true不影响ended,但设为muted后首次play()更容易成功
最常被忽略的是:开发时在桌面 Chrome 点一下就播,上线后嵌在微信 WebView 或 PWA 中,用户没点屏幕就试图自动播,结果 ended 从不触发——不是事件写错了,是根本没播起来。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











