应使用:focus-within兜底并配合tabindex实现触摸设备兼容,用data-open属性+css控制展开逻辑,多级菜单需分层定位,避免overflow:hidden裁剪,注重aria属性与焦点管理以保障可访问性。

hover触发的下拉菜单在触摸设备上完全失效
纯:hover实现的多级菜单,在iOS和Android上点一次没反应,点两次才跳转——因为浏览器把第一次点击当成了“模拟hover”,第二次才触发链接。这不是bug,是规范行为。
解决思路不是“让hover在手机上生效”,而是用:focus-within兜底,并配合tabindex让菜单项可聚焦:
<li class="dropdown"> <a href="#" tabindex="0">产品</a><ul class="submenu">...</ul> </li>- CSS里同时写:
.dropdown:hover .submenu, .dropdown:focus-within .submenu { display: block; } - 移动端用户点开后,键盘Tab也能继续操作,辅助技术友好
click状态需要JavaScript补全,但可以极简
CSS本身没有:click伪类,所以真要靠点击展开,必须用JS。但不用框架,几行就够了:
监听click事件时,只切换一个data-open属性,样式全由CSS控制:
立即学习“前端免费学习笔记(深入)”;
- HTML中给父级加
data-open="false" - JS里:
el.addEventListener('click', () => el.dataset.open = el.dataset.open === 'true' ? 'false' : 'true'); - CSS写:
[data-open="true"] .submenu { display: block; } - 这样逻辑和样式彻底分离,动画、过渡、嵌套层级都还是CSS管
多级嵌套时子菜单定位容易错位
常见错误是所有.submenu都用position: absolute; top: 100%; left: 0;,结果二级菜单叠在一级下面,根本点不到。
正确做法是让每一级自己决定定位基准:
- 一级
.submenu:保持top: 100%; left: 0; - 二级起统一加规则:
.dropdown:hover .submenu, .dropdown[data-open="true"] .submenu { left: 100%; top: -1px; } - 注意
top: -1px是为了对齐边框,避免1px缝隙导致:hover中断 - 如果父级有
overflow: hidden,子菜单会被裁掉——必须去掉或改用clip-path
移动端点击穿透与焦点管理的实际影响
在iOS Safari里,点了下拉项又快速点空白处关闭,有时会误触背后的链接。这不是CSS能解决的,得靠事件控制。
关键不是阻止默认行为,而是让菜单收起时主动移除焦点:
- 收起菜单后,执行
triggerElement.blur() - 避免
pointer-events: none直接隐藏菜单——它会让屏幕阅读器跳过整个结构 - 用
visibility: hidden; opacity: 0;+transition,再配aria-hidden="true" - 真正在意可访问性的话,
role="menu"和role="menuitem"比视觉效果重要得多
最麻烦的从来不是怎么让它动起来,而是动完之后焦点停在哪、屏幕阅读器怎么读、缩放200%时文字会不会撑破容器——这些没法用一套CSS通吃。










