核心思路是同一套DOM靠CSS断点控制显隐:小屏显示下拉菜单、隐藏Tab栏,大屏反之;用:checked+~实现纯CSS下拉,需input与label正确配对且结构紧邻;无障碍需配合aria-hidden和tabindex;移动端需touch-action和overscroll-behavior防穿透。

用 @media 控制 Tab 和下拉菜单的显示切换
核心思路不是写两套组件,而是同一套 DOM 结构,靠 CSS 控制不同断点下的视觉形态。关键在隐藏/显示逻辑必须互斥,且不依赖 JS 切换类名——否则小屏下点击下拉后,大屏恢复时状态错乱。
常见错误是只用 display: none 隐藏下拉菜单,但没给 Tab 栏加 display: none(或相反),导致小屏下两个都显示,或者大屏下下拉残留。
- 小屏(如
max-width: 768px):显示.dropdown-menu,隐藏.tab-list - 大屏:显示
.tab-list,隐藏.dropdown-menu - 确保两者的触发区域(按钮/下拉 toggle)有相同语义,比如都用
role="tab"或role="menuitem",方便无障碍访问
:checked + ~ 选择器实现纯 CSS 下拉展开
不用 JS 就能完成下拉菜单的显隐控制,前提是结构可控:把 <input type="checkbox"> 放在最前,用相邻兄弟选择器影响后续元素。这个方案兼容到 IE9+,比 :focus-within 更稳。
容易踩的坑是 label 没正确绑定 input,或层级被其他 z-index 盖住,导致点击无响应。
立即学习“前端免费学习笔记(深入)”;
-
<input id="tab-toggle" type="checkbox">必须和<label for="tab-toggle">配对 - 下拉菜单需紧跟在
input后面,才能用input:checked ~ .dropdown-menu选中 - 避免在父容器上设
overflow: hidden,否则下拉内容可能被裁剪
<input id="tab-toggle" type="checkbox"> <label for="tab-toggle">☰ Menu</label> <div class="dropdown-menu"> <a href="#tab1">Tab 1</a> <a href="#tab2">Tab 2</a> </div>
Tab 切换的焦点与可访问性不能只靠 display: none
单纯用 display: none 隐藏非活动 Tab 面板,会导致屏幕阅读器跳过内容,键盘 Tab 键也无法聚焦到其中链接或表单。必须配合 aria-hidden="true" 和 tabindex="-1" 才算完整。
而下拉菜单里的选项,要用 role="menu" + role="menuitem",并确保按 ↑/↓ 键能移动焦点——这一步纯 CSS 做不到,得加极简 JS,但仅限键盘导航逻辑,不影响核心显隐。
- 非激活 Tab 面板:同时加
style="display: none"、aria-hidden="true"、tabindex="-1" - 激活面板:移除以上三者,或设
aria-hidden="false" - 下拉菜单收起时,
input[type="checkbox"]应为未勾选态,避免键盘用户按空格意外展开
移动端点击穿透和滚动冲突要手动关掉
小屏下下拉菜单弹出后,如果背景内容可滚动,手指滑动可能误触到底层链接,或菜单跟着页面一起滚走。这不是 bug,是浏览器默认行为。
解决方法不是加 position: fixed(会脱离上下文流,定位难控),而是用 touch-action: none 锁定下拉区域的手势,再用 overscroll-behavior: contain 阻止滚动穿透。
- 给下拉容器加
touch-action: none,禁用缩放/双指滑动 - 给
body加overscroll-behavior: contain,防止下拉弹出时滚动背景 - 注意 iOS Safari 对
overscroll-behavior支持较晚(iOS 16+),老版本需回退到 JS 阻止touchmove










