用<audio>的loop属性只能循环单个文件,实现播放列表循环需JavaScript监听ended事件、维护索引、动态切换src并处理移动端自动播放限制与preload策略。

用 <audio> 的 loop 属性只能循环单个文件,不能自动播列表
很多人试过直接给 <audio> 加 loop,发现只是把当前音频反复播放,根本不会跳到下一个。这是因为原生 loop 是布尔属性,只控制“是否重播当前源”,和播放列表无关。真要实现列表循环(比如播完 song1 → song2 → song3 → 回 song1),必须靠 JavaScript 主动管理。
监听 ended 事件 + 手动切换 src 是最稳妥的做法
浏览器在音频播完时会触发 ended 事件,这是切入播放列表逻辑的唯一可靠时机。别依赖 timeupdate 或定时器去“猜”是否结束——精度差、易出错、移动端尤其不准。
- 把音频路径存在数组里,例如:
const playlist = ["a.mp3", "b.mp3", "c.mp3"]; - 用一个索引变量(如
let currentIndex = 0;)跟踪当前播放项 - 每次
ended触发后,递增索引并取模:currentIndex = (currentIndex + 1) % playlist.length; - 然后设置
audio.src = playlist[currentIndex];并调用audio.play();
注意:首次加载后需调用 audio.load() 再 play(),否则部分浏览器(尤其是 Safari)可能静音或报错 NotAllowedError。
移动端自动播放限制会让列表循环“卡住”
iOS 和 Android Chrome 都要求用户至少有一次交互(如点击按钮)后,才能调用 play()。这意味着:如果页面一打开就试图自动播第一个音频,或在 ended 后立刻 play() 下一个,大概率失败且无提示。
立即学习“前端免费学习笔记(深入)”;
- 解决方案是:只在用户显式操作(比如点“播放”按钮)后才启动播放器,并把整个列表逻辑绑定在这个初始动作上
- 避免在
DOMContentLoaded或load事件里调用play() - 对
audio.play()做catch处理,不假设它一定成功:audio.play().catch(e => console.warn("Auto-play prevented:", e));
preload="none" 能省流量,但首次切换会有延迟
默认 preload="metadata" 会预加载音频头信息(时长、封面等),但不会下载全部数据;设为 "none" 则完全不预加载,等真正 src 被赋值后才开始请求。这对长列表或大文件很实用,但代价是每次切歌时有明显等待(尤其弱网)。
- 如果列表短、文件小,用
preload="auto"或保留默认更顺滑 - 若想折中,可在用户点“下一首”前就预加载下一项:
const nextAudio = new Audio(playlist[nextIndex]); nextAudio.preload = "metadata";(不调play(),仅预加载元数据) -
preload对是否循环本身没影响,但它直接影响用户体验是否“卡顿”
列表循环真正的复杂点不在 HTML 标签怎么写,而在于事件时机、移动端策略、以及网络不可靠性——这些都得在 JS 层兜底,不是加个属性就能跑通的。



















