必须等待 loadedmetadata 事件触发后才能准确获取 audio.duration,该事件在浏览器解析完音频头信息后立即触发;duration 初始为 NaN,流式音频可能返回 Infinity,动态换源需重新监听且注意跨域限制。

用 <audio> 标签插入音频,但时长不能直接从 HTML 属性读取
HTML 的 <audio> 标签本身不提供可读的时长属性(比如 duration 不是 DOM 属性,初始值是 NaN)。你必须等音频元数据加载完成,才能通过 JavaScript 读取 duration。常见错误是:在 load 事件前就访问 audio.duration,结果拿到 NaN 或 Inf。
正确做法是监听 loadedmetadata 事件——它在浏览器解析完音频头信息(含时长、采样率等)后触发,比 load 更早、更可靠:
<audio id="myAudio" src="song.mp3"></audio>
<script>
const audio = document.getElementById('myAudio');
audio.addEventListener('loadedmetadata', () => {
console.log('时长(秒):', audio.duration); // 如 214.76
});
</script>
duration 返回值的含义和常见陷阱
audio.duration 是只读浮点数,单位为秒。但它在不同状态下行为差异大:
- 音频未加载或加载失败 → 值为
NaN - 流式音频(如 HLS、无 Content-Length 的 MP3)→ 可能返回
Infinity(表示未知总长) - 本地文件或完整 HTTP 响应 → 返回准确秒数(含小数)
- 音频尚未触发
loadedmetadata→ 即使已设置src,也仍为NaN
不要用 audio.readyState 判断是否能读时长——readyState === 1(HAVE_METADATA)才安全;readyState === 0(HAVE_NOTHING)时 duration 必为 NaN。
立即学习“前端免费学习笔记(深入)”;
使用 PptxGenJS 和 Python 解析,将结构化的 HTML/CSS 幻灯片内容转换为原生、可编辑的 PowerPoint(.pptx)演示文稿。
动态设置 src 后如何重新获取时长
如果用 JavaScript 动态修改 audio.src(比如切换歌曲),之前的 loadedmetadata 事件不会再次触发,必须手动监听新加载过程:
- 每次改
src后,重新绑定loadedmetadata事件(或用事件委托) - 避免重复绑定:先
removeEventListener,或用{ once: true } - 注意跨域音频:若
src是跨域资源且服务器没配Access-Control-Allow-Origin,loadedmetadata不会触发,duration永远NaN
推荐写法:
function loadAudio(src) {
const audio = document.getElementById('myAudio');
audio.src = src;
audio.addEventListener('loadedmetadata', () => {
console.log('新音频时长:', audio.duration);
}, { once: true });
}
loadAudio('track2.ogg');
服务端提前提供时长,避免前端等待
对用户体验敏感的场景(如列表预渲染时长),别依赖前端加载后计算。更稳的方式是服务端在返回音频 URL 时,一并返回已知时长(例如 API 返回 { url: "a.mp3", duration: 198.3 })。前端直接使用,无需等待加载。
如果必须前端算,注意移动端 Safari 对 loadedmetadata 的兼容性较好,但某些安卓 WebView(尤其旧版)可能延迟触发或漏发事件——加个超时兜底更稳妥:
let durationTimeout = setTimeout(() => {
if (isNaN(audio.duration)) console.warn('时长获取超时,fallback 到 0');
}, 3000);
真正复杂的地方不在怎么写那几行 JS,而在于你得清楚:时长不是“有音频就有”,它是元数据、网络、CORS、浏览器实现共同作用的结果。漏掉任一环节,duration 就是 NaN。


















