移动端折叠展开不能只靠display:none切换,须结合视口约束、touch-action隔离与可访问性控制;details+summary为最轻量语义化方案,checkbox hack可持久化状态,动画应优先使用transform而非width或left。

移动端折叠展开不能只靠 display: none 切换,必须结合视口约束、触摸行为隔离和可访问性控制;页面适配不是“让内容缩进屏幕”,而是让交互在小屏上仍可触达、可聚焦、不误操作。
用 details + summary 实现无 JS 折叠
这是最轻量、语义正确、键盘和屏幕阅读器友好的方案,适合 FAQ、设置项、帮助条目等静态结构。
-
summary必须是details的第一个子元素,否则点击无效 - 默认关闭,加
open属性可设为初始展开:<details open></details> - 原生不带动画,但可用
max-height+transition模拟(注意:不能用height: auto) - Safari 对
::marker支持不稳定,统一用summary::before替代箭头图标 - IE 完全不支持,需做特性检测:
if ('open' in document.createElement('details'))
JS 控制的折叠必须处理 touch-action: pan-y
移动端点击折叠按钮时,手指常带轻微纵向滑动,若未禁用默认滚动行为,会触发页面滚动而非点击,导致菜单“点不中”。
- 给折叠容器(如侧边栏、下拉菜单父元素)加
touch-action: pan-y - 若容器内含可滚动区域(如长列表),其子元素需单独设
touch-action: auto,否则无法滚动 - 不要依赖
preventDefault()拦截 touchstart —— 它会破坏所有原生滚动和缩放 - 在 iOS Safari 中,
touch-action是硬性要求,不加就大概率点失效
折叠状态要能跨刷新保留,别只靠 JS 变量
用户收起菜单后刷新页面又弹开,体验断裂。纯 CSS 无法持久化状态,但不用 localStorage 也能解决。
立即学习“前端免费学习笔记(深入)”;
- 用
<input type="checkbox">配合兄弟选择器(checkbox hack),它是唯一原生支持状态持久化的 HTML 元素 -
input必须放在 DOM 最前,紧挨label,后续被控元素用:checked ~ .target控制 - 折叠时用
display: none,不是visibility: hidden或opacity: 0—— 后两者仍占 tab 顺序 - 同步加
tabindex="-1"和aria-hidden="true",否则键盘用户会聚焦到不可见区域 - 移动端小屏下,强制子菜单
display: none !important,一级热区尺寸不低于44px × 44px
响应式折叠菜单的宽度与定位必须解耦
很多实现用 margin-left 手动推主内容,结果一换屏就错位;或用 width 动画侧边栏,导致卡顿跳变。
- 侧边栏用
position: fixed或absolute,避免影响文档流;主内容用padding-left替代margin-left - 动画用
transform: translateX(),不是left或width—— 前者走 GPU 加速,不触发重排 - 隐藏态写
transform: translateX(-100%),别用left: -300px—— 后者依赖像素值,响应式下失效 - 用
flex解耦宽度:侧边栏flex: 0 0 240px,折叠时切 class 改为flex: 0 0 60px;主内容用flex: 1 - 父容器必须设
display: flex; min-height: 100vh,否则 sticky 或高度塌陷问题频发
真正难的不是“怎么让菜单动起来”,而是折叠后焦点是否归位、屏幕阅读器能否识别状态、小屏下热区是否够大、刷新后是否还记得用户意图——这些细节没对齐,再顺滑的动画也等于白做。



















