多层级faq折叠面板不能嵌套,必须用语义化+css+js实现;原因包括规范不支持、浏览器行为不一致、键盘焦点与屏幕阅读器异常;正确做法是嵌套表达层级、事件委托控制展开、用:has()+>精准控制显示。

多层级 FAQ 折叠面板不能靠嵌套 <details></details> 实现——规范不支持,浏览器行为不一致,键盘焦点和屏幕阅读器会出问题。真要多级,必须用语义化 <ul></ul> + CSS + JavaScript 控制,<details></details> 只适合单层。
为什么不能嵌套 <details></details>
强行在 <summary></summary> 里放另一个 <details></details>,会出现三种典型症状:
• 点开二级时一级自动收起
• Tab 键跳过某级、焦点卡死
• Safari 中 details[open] summary::before 旋转图标渲染错乱
根本原因是 <details></details> 的 open 属性无法独立控制嵌套层级,且原生不承认子 <details></details> 是合法子元素。
<ul></ul> 多级结构必须这么写
层级关系靠嵌套 <ul></ul> 表达,不能用 <div> 模拟;否则辅助技术读不出父子关系,SEO 和键盘导航全失效。
<ul>
<li>
<code><li> 必须直接包裹触发文本(如 <span class="toggle"></span>),子 <ul></ul> 紧跟其后,中间不能有空格或换行
class="nested",避免 CSS 后代选择器误匹配深层项<ul class="faq-menu"></ul> 别设 display: flex,会破坏列表缩进和 tab 键顺序JavaScript 控制展开/收起的关键点
用事件委托绑定到父容器,而不是每个 <li> 单独监听——DOM 动态增删时不会漏绑或重复绑。
- 监听
click时,用event.target.closest(".toggle")获取触发元素,再找它最近的<li> - 只切换当前项的
active类,不操作兄弟或祖先节点 - 移动端需额外监听
touchstart并调用preventDefault(),否则 iOS 触发两次 click - 内容区高度动画别硬写
max-height: 500px—— 高度不确定时,用content.scrollHeight动态读取后赋值
CSS 层级显示必须用 :has() + >
写 .active ul { display: block } 会导致所有子级一起弹出。真正隔离层级,得靠组合选择器:
-
.faq-item:has(> .nested) > .toggle::after:只给「直接含 .nested」的项加下拉箭头 -
.faq-item.active > .nested:只展开当前项的直系子菜单,不穿透到孙子级 - 禁用
visibility: hidden做动画——它仍占布局空间,点击区域会偏移 -
<ul class="nested"></ul>默认设display: none,激活时才display: block
最易被忽略的是:多级折叠必须确保每层 <li> 都有唯一 ID,锚点跳转(比如 URL 带 #how-to-start)才能准确定位并展开对应项;ID 不能含空格或中文,建议用小写拼音或 encodeURIComponent() 处理后再截取。











