不能直接用display: none切换,因其无过渡动画且移除元素于无障碍树,损害可访问性;需用aria-expanded、aria-controls、role="navigation"等ARIA属性建立语义关联,并以max-height过渡实现平滑展开收起。

移动端折叠菜单为什么不能直接用 display: none 切换?
因为纯 CSS 的 display: none 切换没有过渡动画,用户感知突兀;更关键的是,它会让屏幕阅读器跳过整个菜单结构,损害可访问性。真实项目中必须兼顾触控响应、键盘导航和屏幕阅读器支持。
推荐方案是用 aria-expanded 控制语义状态,配合 max-height 或 transform 实现带过渡的展开收起,同时保持 DOM 始终存在。
HTML 结构必须包含哪些 ARIA 属性?
一个可访问的折叠菜单, 触发器和 内容容器之间要有明确的控制关系:
-
button必须有aria-expanded="false"(初始状态)和aria-controls="menu-id" -
nav必须有对应id="menu-id"和role="navigation" - 按钮文本建议用「菜单」或图标 +
aria-label,避免仅用「☰」
示例片段:
<button aria-expanded="false" aria-controls="main-menu">菜单</button> <nav id="main-menu" role="navigation">...</nav>
用 max-height 过渡时为什么设成固定值?
因为 max-height: 0 → max-height: 500px 是目前最兼容的过渡方式(height: auto 无法过渡)。500px 要略大于菜单最大可能高度,否则动画会截断。
常见坑:
• 设太小:内容撑出、滚动条意外出现
• 设太大:收起动画变慢、视觉延迟明显
• 忘记加 overflow: hidden:内容在收起过程中仍可见
CSS 关键部分:
nav[aria-hidden="true"] {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}
nav[aria-hidden="false"] {
max-height: 500px; /* 根据实际内容调整 */
}
JavaScript 只需切换两个属性,但顺序很重要
点击按钮后,必须先更新 aria-expanded,再切换 aria-hidden(或 class),否则屏幕阅读器可能读到错误状态。
实操要点:
• 用 element.setAttribute() 而非 .ariaExpanded =(IE 不支持后者)
• 检查 event.target 是否为按钮本身,防止事件冒泡误触发
• 移动端要监听 touchstart 或 click(iOS Safari 对 click 有 300ms 延迟,但现代已基本修复)
最小可用脚本:
document.querySelector('button').addEventListener('click', function() {
const isExpanded = this.getAttribute('aria-expanded') === 'true';
this.setAttribute('aria-expanded', !isExpanded);
document.getElementById(this.getAttribute('aria-controls')).setAttribute('aria-hidden', isExpanded);
});
真正难的不是展开动画,而是确保每次点击后焦点管理正确、键盘 Tab 能进入菜单项、ESC 键能关闭——这些细节在真机测试时最容易暴露。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











