为什么Safari 15无法触发audio.onended等音频事件?该如何修复?
Safari 15
audio.onended事件不触发修复方案 该问题的核心触发原因是Safari 15对HTMLMediaElement的事件绑定、资源处理逻辑与Chromium内核浏览器存在差异,直接对onended属性赋值事件处理器的写法在该版本下存在兼容缺陷。可按以下优先级尝试修复:
- 方案一:改用
addEventListener绑定事件,同时添加事件清理逻辑避免内存泄漏,替换原有useEffect代码如下:
useEffect(() => { if (audio === null) return const handleAudioEnded = () => console.log('hello') audio.addEventListener('ended', handleAudioEnded) // 组件卸载/依赖更新时清理监听 return () => { audio.removeEventListener('ended', handleAudioEnded) } }, [audio])
该方案解决了直接赋值onended被Safari内置逻辑覆盖、事件无法注册的问题,是优先推荐的修复方式。
- 方案二:显式配置audio元素属性,添加
playsInline声明、禁用loop属性。Safari 15在未显式配置playsInline时会默认调用系统级音频播放器接管播放流程,结束事件不会传回页面层的audio实例。
若为React创建的audio元素,直接在标签上加playsInline={true};原生JS创建的实例可通过audio.playsInline = true手动赋值。 - 方案三:添加兜底的播放进度监听逻辑,针对Safari特殊场景做兼容。如果上述两种方案都无法生效,可通过
timeupdate事件实时判断播放进度,接近音频总时长时手动触发结束逻辑:
useEffect(() => { if (audio === null) return const handlePlayProgress = () => { // 剩余播放时长小于0.1s时判定为播放结束,可根据业务场景调整阈值 if (!audio.paused && audio.duration - audio.currentTime < 0.1) { console.log('hello') audio.pause() } } audio.addEventListener('timeupdate', handlePlayProgress) return () => { audio.removeEventListener('timeupdate', handlePlayProgress) } }, [audio])
额外注意:Safari对音频资源的请求校验更为严格,需要确保音频资源的CORS配置正确,且服务端支持Range范围请求、响应头返回正确的Content-Range字段,资源加载异常也会导致结束事件无法正常触发。
内容的提问来源于stack exchange,提问作者Jeremiah Caballero
相关产品推荐
相关产品推荐

