使用js动态创建audio并监听toggle事件可实现真正可用的折叠式音频列表:展开时加载播放、收起时暂停清理,避免预加载浪费和状态错乱。

<details></details> 本身不支持直接嵌套 <audio></audio> 并自动控制播放状态,但可以配合 <summary></summary> 和少量 JS 实现真正可用的折叠式音频列表——关键不是“能不能放”,而是“展开时要不要自动加载/播放”“点击音频控件会不会意外收起”。
为什么直接写
浏览器默认行为下:<details></details> 展开/收起只影响可见性(display),但 <audio></audio> 元素一旦写在 HTML 里,就会触发预加载(取决于 preload 属性),可能浪费带宽;更糟的是,某些浏览器(如 Safari)在 <details></details> 收起时仍保持 <audio></audio> 的播放状态,用户看不见却还在响。
-
preload="none"是必须加的,否则 Chrome/Firefox 默认预加载元数据甚至音频流 - 收起
<details></details>时,不能靠 CSS 隐藏来暂停——得用 JS 监听toggle事件主动调用pause() -
<summary></summary>点击区域若太小,容易误触音频控件;建议给<summary></summary>加user-select: none防止双击选中
让每个 audio 在 details 展开时才加载并可播放
核心是延迟初始化:HTML 中只放占位结构,JS 在 details.open 变为 true 后才动态创建 <audio></audio> 并插入,避免初始加载。同时绑定 toggle 事件做清理。
<details id="playlist-1"><summary>播客第1期:前端性能陷阱</summary><div class="audio-container"></div> </details>
对应 JS:
- 监听
details#playlist-1的toggle事件 - 若
event.target.open === true且.audio-container内无<audio></audio>,则创建并插入<audio controls preload="none"><source></source></audio> - 若
open === false,查找子<audio></audio>并调用pause(),再移除节点(防止内存泄漏)
多个列表项共用一套逻辑的写法
不用给每个 <details></details> 写独立 ID,用委托更干净:
document.addEventListener('toggle', (e) => {
if (e.target.matches('details')) {
const container = e.target.querySelector('.audio-container');
if (!container) return;
if (e.target.open) {
if (!container.querySelector('audio')) {
const audio = document.createElement('audio');
audio.controls = true;
audio.preload = 'none';
audio.innerHTML = `<source src="%24%7Bcontainer.dataset.src%7D" type="audio/mpeg">`;
container.appendChild(audio);
}
} else {
const audio = container.querySelector('audio');
if (audio) audio.pause();
container.innerHTML = '';
}
}
});</source>
- 注意
dataset.src要确保是合法 URL,相对路径需相对于当前页面 - 如果音频格式不止 MP3,
<source></source>标签需按需补全type,比如audio/ogg - 移动端 Safari 对
preload="none"支持稳定,但 iOS 15 之前有 bug:即使preload="none",首次播放仍可能卡顿——此时可加audio.load()在用户点击播放前显式触发
样式和交互细节容易被忽略
<summary></summary> 默认带箭头,但音频列表需要明确视觉反馈:正在播放的项要高亮,收起后要清空状态。纯 CSS 做不到跨元素状态同步,必须靠 JS 维护。
- 给正在播放的
<details></details>加类名is-playing,用 CSS 控制 summary 文字颜色或加图标 - 不要用
details[open] audio这种选择器判断是否“可播放”——open只表示展开,不代表音频已加载或正在播 - 如果用户快速连续点击 summary,可能触发多次
toggle,需在 JS 里加节流或检查audio.readyState避免重复创建
最麻烦的其实是音频中断逻辑:比如用户点开第二项,第一项应自动暂停。这需要全局维护一个 currentAudio 引用,每次新播放前先 pause() 它——这点纯 HTML/CSS 做不了,必须 JS 主导状态。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











