原生 不支持嵌套,因 html5 规范未定义该行为,导致浏览器兼容性差且外层折叠逻辑中断;可通过 js 监听 toggle 事件手动控制子级显隐,或改用 css+js 手写方案实现真正嵌套。

原生 <details></details> 不支持嵌套,强行写会失效
浏览器对嵌套 <details></details> 的支持极差:Chrome 和 Firefox 会忽略内层的 open 状态,Safari 直接不响应点击;更关键的是,<summary></summary> 必须是 <details></details> 的**首个子元素**,一旦中间插入另一个 <details></details>,外层折叠逻辑就中断。这不是样式问题,而是规范层面的限制 —— HTML5 明确未定义嵌套行为。
<details></details> + JS 监听 toggle 事件可模拟嵌套逻辑
若必须保留原生语义又需要多层折叠,得用 JS 补足缺失能力。核心是监听每个 <details></details> 的 toggle 事件,手动控制子级显隐:
- 给每组父子面板加统一
data-group属性(如data-group="faq-1") - 在父
<details></details>的toggle事件中,用querySelectorAll找到同组子<details></details>,再根据this.open值批量设置其open属性 - 避免直接操作
display或hidden,否则破坏原生可访问性(如aria-expanded同步失效)
示例片段:
document.querySelectorAll('details[data-group]').forEach(panel => {
panel.addEventListener('toggle', () => {
const group = panel.dataset.group;
document.querySelectorAll(`details[data-group="${group}"]`).forEach(child => {
if (child !== panel) child.open = panel.open;
});
});
});
真嵌套需求应改用 CSS + JS 手动方案
当子面板需独立开关、动画不同步、或内容含视频/iframe 等需生命周期管理时,原生 <details></details> 已无优势。此时推荐结构化手写:
- 用
<div class="accordion-item"> 包裹标题与内容,避免语义污染 <li>CSS 控制 <code>max-height过渡(设足够大值如500px,而非auto) - JS 中用
scrollHeight动态读取内容高度,再赋给style.maxHeight实现精准动画 - 为键盘支持,给标题加
tabindex="0",监听Enter/Space键触发切换
注意:多个面板同时展开时,别忘了在 JS 中加互斥逻辑(如 querySelectorAll('.is-open').forEach(el => el.classList.remove('is-open'))),否则变成全开模式。
嵌套时图标和焦点容易出错
自定义箭头用 ::before 或 ::after 时,details[open] summary::after 只能旋转外层图标;子面板的图标状态不会联动 —— 必须靠 JS 切换类名驱动。更隐蔽的问题是焦点:原生 <summary></summary> 自带 role="button" 和键盘支持,但 JS 方案里若用 <div> 或 <code><span></span> 当触发器,必须手动加 role="button"、aria-expanded 和 aria-controls,否则屏幕阅读器无法识别状态变化。这点常被跳过,却直接影响合规性。











