默认details不互斥,需用toggle事件监听并手动控制其他项open属性;仅监听click会忽略键盘操作导致状态不同步;正确做法是在event.target.open为true时移除同组其他details的open属性。

默认不互斥,多个可以同时展开。要实现“只允许展开一项”,必须用 JavaScript 监听 toggle 事件手动控制其他项的 open 属性。
为什么不能只监听 click?
因为 支持键盘操作(空格/回车),仅监听 click 会漏掉这些触发方式,导致屏幕阅读器或键盘用户状态不同步。必须用 toggle 事件——它在所有合法展开/收起动作后都会触发。
常见错误是写成:
el.addEventListener('click', () => el.open = true)
这既没处理收起逻辑,也忽略键盘行为,还可能引发重复 toggle。
如何正确关闭其他 ?
核心是:只在当前项真正打开时(event.target.open === true),遍历同组所有 ,对非当前项调用 .removeAttribute('open')。
- 别用
.open = false—— 这会再次触发toggle事件,造成循环 - 别用
.setAttribute('open', 'false')—— 布尔属性设为字符串"false"仍会被视为真值 - 确保所有
在同一 DOM 容器内,方便querySelectorAll获取
要不要加 CSS 动画?
原生 不支持 height 过渡,但你可以用 max-height + scrollHeight 模拟平滑展开。不过要注意:
- 必须给
设置明确的max-height初始值(如0)和展开值(如500px),不能用auto - 动画只作用于
max-height,内容高度变化时可能有轻微错位 - Safari 15.4+ 才完整支持
::marker自定义箭头,旧版需用appearance: none+background
移动端和无障碍还有哪些坑?
互斥逻辑本身不影响可访问性,但容易忽略两点:
-
必须是的第一个子元素,否则键盘无法聚焦、屏幕阅读器不识别 - 如果用 JS 批量展开/收起(比如“全部展开”按钮),记得同步更新每个
的aria-expanded属性,否则 VoiceOver 或 NVDA 可能播报错误状态 - 不要给
加display: block或移除伪元素——这会让它失去按钮语义和焦点能力
最易被忽略的是:互斥不是浏览器行为,而是你代码里那一行 .removeAttribute('open') 决定的。删掉它,或者监听错事件,整个手风琴就退化成一堆独立开关。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











