audio标签需提供多格式source并声明type属性以实现跨浏览器兼容,autoplay必须配合muted属性,play()需捕获Promise拒绝,动态切换音频时须调用load()重置状态。

audio 标签必须提供多格式 <source> 才能跨浏览器兼容
Chrome、Firefox、Safari 对音频格式支持不一致:Chrome 和 Safari 基本认 mp3,Firefox 默认不支持 mp3 但支持 ogg,Safari 还可能拒绝未声明 type 的 <source>。只写一个 src 属性等于主动放弃部分用户。
正确做法是用多个 <source> 并按优先级从上到下排列,浏览器会跳过不支持的格式继续尝试:
<audio id="player"> <source src="song.mp3" type="audio/mpeg"> <source src="song.ogg" type="audio/ogg"> <source src="song.wav" type="audio/wav"> 您的浏览器不支持 audio 标签。 </audio>
-
type属性不能省——它让浏览器免于下载文件就能快速判断是否支持 - MP3 放最前(兼容性最广),OGG 次之(开源浏览器友好),WAV 最后(体积大,仅作兜底)
- 不要依赖
canPlayType()动态加载——它返回值模糊("probably"/"maybe"),且无法替代真实<source>尝试
autoplay 失效时,muted 是唯一可靠绕过方案
所有现代浏览器(Chrome 66+、Firefox 70+、Safari 14+)都禁止有声 autoplay,哪怕页面已获得焦点。试图用 play() 在 load 或 DOMContentLoaded 事件中触发,大概率抛出 NotAllowedError。
真正有效的解法只有两个条件同时满足:
立即学习“前端免费学习笔记(深入)”;
- 在
<audio>标签上显式加muted属性(不是靠 JS 后设) - 再配合
autoplay,例如:<audio muted autoplay>
这样首次加载就能静音播放;后续用户交互(如点击按钮)再调用 audio.muted = false 解除静音。硬靠 JS 模拟“用户已交互”是无效的——浏览器只认真实的 pointer/click/key 等事件。
用 play() 必须处理 Promise 拒绝,否则控制台静默失败
audio.play() 不再是同步操作,它返回 Promise。失败时不会抛异常,而是 reject,且默认不报错——你点按钮没反应,控制台却空空如也。
必须显式 catch:
audio.play().catch(err => {
console.warn('播放失败:', err.name); // 常见 err.name: "NotAllowedError", "NotSupportedError"
// 可在此触发 UI 提示,比如显示“请先点击页面任意位置”
});
- 常见失败原因:未满足 autoplay 条件、格式不被支持、文件 404、CORS 限制(跨域音频需服务端配
Access-Control-Allow-Origin) - 不要在
click回调里直接写audio.play()就完事——漏掉 catch 等于放弃调试线索 - 移动端尤其敏感:iOS Safari 要求用户手势(tap)必须直接触发
play(),嵌套在 setTimeout 或 fetch 回调里会失效
动态切换音频时,src 赋值后必须手动调用 load()
很多人以为改了 audio.src = 'new.mp3' 就能直接 play(),结果播的还是旧文件,或报错 InvalidStateError。这是因为浏览器缓存了之前的加载状态,新 src 需要重新初始化解码器。
正确流程是三步:
- 设置新
src - 立即调用
audio.load()—— 它重置内部状态,清空缓冲,准备加载新资源 - 再调用
audio.play()(仍需处理 Promise)
例如切换歌曲:
function playSong(url) {
const audio = document.getElementById('player');
audio.src = url;
audio.load(); // 关键!没有这行,90% 情况会失败
audio.play().catch(e => console.error(e));
}
注意:load() 是同步方法,但它触发的是异步加载过程;所以 play() 仍要等加载足够数据才能成功,可监听 canplay 事件做更精细控制。
兼容播放真正的难点不在写法,而在浏览器策略的层层限制——格式、静音、Promise、load() 这四环缺一不可,漏掉任一环节,用户就卡在“点不动”的状态里。



















