原生 details/summary 可实现无 js 折叠菜单,summary 必须为 details 的第一个子元素;需用 class 控制显隐并同步 aria-expanded 和 aria-hidden 以保障可访问性。

用 details + summary 最简实现折叠菜单
原生 HTML 就有折叠组件,不用 JS 也能控制 div 显示隐藏:details 是容器,summary 是可点击的标题栏。点一下展开,再点收起,浏览器自带状态管理,连 aria-expanded 都不用手动维护。
常见错误是把 summary 当成普通按钮乱加 onclick,或者套在 div 外面破坏语义结构。正确写法必须是 summary 为 details 的**第一个子元素**:
<details><summary>菜单标题</summary><div>这里是折叠内容,可以放任意 HTML</div> </details>
-
details默认关闭,加open属性可默认展开:<details open></details> - 不支持 IE,但 Chrome/Firefox/Safari/Edge 全支持(包括移动端)
- 样式可定制:用
summary::marker控制小箭头,details[open] > div选中展开时的内容区
用 classList.toggle() 手动控制显示隐藏
需要兼容 IE 或自定义交互逻辑(比如点击其他区域收起、动画过渡)时,就得用 JS 操作 display 或 visibility。核心是切换一个 class,让 CSS 控制显隐,而不是直接改 style.display —— 这样更易维护、可复用、支持 CSS 过渡。
典型错误是只改 display 却忽略屏幕阅读器需求,或用 show()/hide() 这类 jQuery 遗留写法(现代项目基本不配 jQuery):
<button onclick="toggleMenu('myMenu')">展开菜单</button>
<div id="myMenu" class="menu-content">...</div>
CSS 配合:
.menu-content { display: none; }
.menu-content.active { display: block; }
JS 实现:
function toggleMenu(id) {
const el = document.getElementById(id);
el.classList.toggle('active');
}
- 用
aria-expanded和aria-controls补全可访问性,例如:<button aria-expanded="false" aria-controls="myMenu"></button> - 避免用
visibility: hidden,它仍占布局空间;要用display: none或配合max-height做渐变收起 - 如果菜单内含表单或焦点元素,展开后建议
el.querySelector('input, button').focus()自动聚焦
用 aria-expanded 和 aria-hidden 做可访问性兜底
光靠视觉显隐不够,屏幕阅读器需要明确的状态提示。仅靠 display: none 不会自动通知辅助技术内容已隐藏,必须手动同步 ARIA 属性。
错误做法是忘记更新 aria-expanded,或对同一个元素同时设 aria-hidden="true" 和 aria-expanded="true"(语义冲突):
- 按钮上始终要有
aria-expanded,值为"true"或"false",随菜单状态实时更新 - 折叠内容容器加
aria-hidden="true"(隐藏时)或移除该属性(显示时),不能写成aria-hidden="false"(无效值) - 初始状态要一致:按钮
aria-expanded="false"→ 内容aria-hidden="true"→ CSS 为display: none
为什么不用 hidden 属性?
hidden 是 HTML5 布尔属性,浏览器会默认应用 display: none,看似省事。但它有两个硬伤:
- 无法用 CSS 选择器精准控制过渡动画,比如
[hidden] { opacity: 0; transition: opacity 0.2s; }不生效 - JS 切换时容易遗漏同步 ARIA 状态,
el.hidden = false不会自动删掉aria-hidden - 某些旧版 Safari 对
hidden的渲染有微小延迟,导致闪动
所以生产环境更推荐「class 控制 + ARIA 同步」这套组合,可控性强,调试方便,也更容易写单元测试。
真正麻烦的不是怎么写,而是每次新增一个折叠模块时,是否记得补全 aria-expanded、是否检查了键盘 Tab 流、是否验证了移动端点击穿透——这些细节堆起来,才决定用户到底觉得这是个菜单,还是个摆设。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











