:checked 适合移动端标签切换导航,因其无需 js 即可通过 css 捕获复选框状态,结合兄弟选择器控制菜单显隐,支持无障碍访问;需确保 input 与菜单同级、id 唯一、隐藏 input 但保留交互性,并用 max-height + overflow: hidden 实现平滑过渡动画。

为什么 :checked 适合做移动端标签切换导航
因为移动端菜单常需「点击展开/收起」且不依赖 JS,而 <input type="checkbox"> 的 :checked 状态天然可被 CSS 捕获,配合兄弟选择器(~ 或 +)就能控制后续元素显隐——零 JS、语义清晰、支持键盘焦点和屏幕阅读器。
关键前提是:把触发控件(<input>)和内容容器放在同一父级下,且内容紧跟在 input 后面(用 +)或同级后方(用 ~)。
:checked + .menu-panel 的 DOM 结构必须严格对齐
常见错误是 input 和菜单容器不在同一层级,或中间插入了其他元素,导致 + 失效。正确结构如下:
<nav class="mobile-nav"><input type="checkbox" id="nav-toggle"><label for="nav-toggle">☰</label>
<div class="menu-panel">
<a href="#home">首页</a>
<a href="#about">关于</a>
</div>
</nav>
此时可用 #nav-toggle:checked + .menu-panel 控制显示;若 label 在 input 前,就得用 #nav-toggle:checked ~ .menu-panel,但要注意 ~ 会匹配所有后续同级元素,可能误触其他模块。
- input 必须有
id,label 必须用for关联它,否则点击 label 不会触发状态切换 - input 默认要加
position: absolute; opacity: 0; pointer-events: none;隐藏但保留可交互性 - .menu-panel 初始应设
display: none;或max-height: 0;配合过渡动画
用 max-height 实现带过渡的展开动画更可靠
直接用 display: none ↔ block 无法过渡,所以得用 max-height 模拟。注意:不能设 max-height: 0; → max-height: 500px; 这种固定值,否则内容高度变化时会露馅或留白。
推荐做法是预估最大行数(比如 6 项 × 行高 48px ≈ 288px),再略留余量:
#nav-toggle:not(:checked) ~ .menu-panel {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}
#nav-toggle:checked ~ .menu-panel {
max-height: 320px; /* 足够容纳所有项,又不至于过大 */
}
- 务必加
overflow: hidden,否则内容会溢出未展开区域 - 过渡时间别超过 0.4s,否则在低端安卓机上易卡顿
- 如果菜单项含图片或字体加载延迟,首次展开可能高度计算不准——此时改用
height: auto不可行,只能接受略长的max-height值
真机测试时容易忽略的触摸与焦点问题
在 iOS Safari 和部分安卓浏览器中,:checked 状态切换后,若 label 没正确绑定,或 input 被 pointer-events: none 错误覆盖,会导致点击无响应。
- label 必须包裹图标文字,且不要给 label 设
pointer-events: none - input 的
opacity: 0要配合z-index: 1确保它仍在点击层最上方 - 测试时用真机点 label 文字区域,而非只点图标——有些用户习惯点文字触发,而图标可能没占满 label 尺寸
- 如果菜单里有
<a></a>标签,记得加tabindex="0"并确保 focus 样式可见,否则键盘用户无法操作
真正麻烦的是当页面同时存在多个同类菜单时,ID 冲突或 CSS 选择器范围过宽,会让一个开关控制多个面板——每个 input 的 id 和关联的 for、选择器都得唯一。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











