纯css实现汉堡菜单需将checkbox置于dom最前以支持兄弟选择器,用transform:translatex()替代left避免重排,设100dvh防safari地址栏干扰,并严格控制pointer-events与触控热区。

纯 CSS 实现汉堡菜单的展开收起,完全可行,但“优雅”不等于“随便写几个 :checked 就完事”——它取决于结构顺序、选择器路径、动画属性选型,以及是否真正绕开了移动端常见陷阱。
为什么 input[type="checkbox"] 必须放在 DOM 最前面
这是绝大多数失败案例的根源。CSS 的兄弟选择器 ~ 和相邻选择器 + 只能向后匹配,不能向前。如果 <input id="menu-toggle"> 被插在 <nav></nav> 后面或中间,#menu-toggle:checked ~ .menu__box 就永远不生效。
-
<input>必须是导航相关元素中第一个节点(推荐放开头) - 所有被控制元素(图标、遮罩、菜单容器)必须严格位于该
input之后 - 不要用
display: none隐藏它——改用position: absolute; opacity: 0; pointer-events: none; - 用
<label for="menu-toggle"></label>关联点击区域,确保键盘Enter和屏幕阅读器可操作
transform: translateX() 比 left 更稳,但得配对使用
直接改 left 触发重排(layout),尤其在低端安卓 WebView 中卡顿明显;translateX() 走合成层,顺滑得多——但只换属性不够,还得控制渲染上下文。
- 初始状态写
transform: translateX(-100%);,不是-100vw(避免 Safari 地址栏高度干扰) - 激活状态写
transform: translateX(0);,配合transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1); - 菜单容器加
will-change: transform;(仅动画期间启用,别全局写) - 若仍偏移,补一句
transform: translateZ(0);强制新建合成层
移动端点击无响应?先查热区和 pointer-events
不是 JS 没绑,而是按钮根本没被点中。iOS 要求最小触控区域 ≥ 44×44pt,安卓 WebView 对 opacity: 0 元素的点击穿透处理也不一致。
-
<label></label>必须设display: block; width: 44px; height: 44px; -
input隐藏时务必加pointer-events: none;,否则某些安卓机型会拦截后续点击 - 遮罩层(如半透明背景)默认设
pointer-events: none;,激活时再切为auto - 菜单内容区(
.menu__links)始终保留pointer-events: auto;,否则链接点不动
100dvh 替代 100vh 是 iOS 安全底线
Safari 在地址栏收起/展开时会动态调整 vh 值,导致菜单高度突然收缩或溢出。这不是 bug,是规范行为。
- 菜单容器高度优先用
height: 100dvh;(2022 年起已广泛支持) - 若需兼容老版 Safari,fallback 方案:用
min-height: 100vh; height: 100vh;+ JS 动态修正(但这就破了纯 CSS 前提) - 同时给
html, body加overflow-x: hidden;,防止滚动条挤占宽度影响100%计算
真正难的不是让菜单动起来,而是让它在各种 WebView 下都“刚好贴边、刚好点中、刚好撑满、刚好不闪”。这些细节不写进 CSS 规则里,光靠“看着差不多”撑不过真机测试。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











