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

多层级 FAQ 折叠面板不能靠嵌套 <details> 实现——规范不支持,浏览器行为不一致,键盘焦点和屏幕阅读器会出问题。真要多级,必须用语义化 <ul> + CSS + JavaScript 控制,<details> 只适合单层。
为什么不能嵌套 <details>
强行在 <summary> 里放另一个 <details>,会出现三种典型症状:
• 点开二级时一级自动收起
• Tab 键跳过某级、焦点卡死
• Safari 中 details[open] summary::before 旋转图标渲染错乱
根本原因是 <details> 的 open 属性无法独立控制嵌套层级,且原生不承认子 <details> 是合法子元素。
<ul> 多级结构必须这么写
层级关系靠嵌套 <ul> 表达,不能用 <div> 模拟;否则辅助技术读不出父子关系,SEO 和键盘导航全失效。
-
<li>必须直接包裹触发文本(如<span class="toggle">),子<ul>紧跟其后,中间不能有空格或换行 - 每层子菜单加
class="nested",避免 CSS 后代选择器误匹配深层项 - 顶层
<ul class="faq-menu">别设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">默认设display: none,激活时才display: block
最易被忽略的是:多级折叠必须确保每层 <li> 都有唯一 ID,锚点跳转(比如 URL 带 #how-to-start)才能准确定位并展开对应项;ID 不能含空格或中文,建议用小写拼音或 encodeURIComponent() 处理后再截取。



















