二级下拉菜单必须将子ul嵌套在父li内,使用li:hover > ul触发显示,设父li为position: relative、子ul为position: absolute且top: 100%,移动端需JS添加open类并同步aria-expanded。

二级下拉菜单必须用嵌套 ul,不能平级或外置
子菜单必须是父级 li 的直接子元素,否则 :hover 或 JS 逻辑会失效。常见错误是把二级 ul 写在父 li 外面,或者用 div 包裹二级内容——这会破坏语义、影响可访问性,也导致 CSS 定位错乱。
正确结构示例:
<nav>
<ul class="main-nav">
<li><a href="#">首页</a></li>
<li class="has-sub">
<a href="#">产品</a>
<ul class="submenu">
<li><a href="/laptops">笔记本</a></li>
<li><a href="/desktops">台式机</a></li>
</ul>
</li>
</ul>
</nav>-
class="has-sub"是可选但推荐的标记,方便后续 JS 控制或 CSS 精准定位 - 不要给
ul设margin或padding,否则定位偏移;统一用* { margin: 0; padding: 0; }重置 - 移动端不支持
:hover,纯 CSS 方案在 iOS/Android 上默认不生效
li:hover > ul 是最安全的显示触发器
用 li:hover > ul(子选择器)只展开下一层,避免三级菜单意外弹出;用 li:hover ul(后代选择器)会递归展开所有嵌套层,容易失控。
关键 CSS 片段:
立即学习“前端免费学习笔记(深入)”;
.main-nav li {
position: relative;
}
.main-nav li ul {
position: absolute;
top: 100%;
left: 0;
display: none;
min-width: 200px;
background: #fff;
border: 1px solid #ddd;
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}
.main-nav li:hover > ul {
display: block;
}- 必须设
position: relative在父li上,否则position: absolute的子ul会相对于body定位 -
top: 100%比top: 40px更可靠——它自动贴合父项高度,不依赖固定值 - 别漏掉
min-width,否则窄文字会让下拉框过窄,影响点击区域
移动端必须加 JS 切换类,不能只靠 :hover
iOS Safari 和 Android Chrome 对 :hover 的支持极弱:触摸后短暂触发、不维持、或完全忽略。纯 CSS 下拉菜单在手机上基本不可用。
最小可行 JS 方案:
document.querySelectorAll('.has-sub > a').forEach(el => {
el.addEventListener('click', e => {
e.preventDefault();
const parent = el.parentElement;
parent.classList.toggle('open');
});
});配套 CSS:
.main-nav li.open > ul,
.main-nav li:hover > ul {
display: block;
}- 别用
touchstart替代click,它会和页面滚动冲突,用户滑动时可能误触菜单 - 如果用了
aria-expanded,记得同步更新:el.setAttribute('aria-expanded', parent.classList.contains('open')) - IE8–IE10 不支持
:hover在li上,得退化为a:hover + ul,此时ul必须紧跟a,且a要设display: block
悬停中断问题:间隙会导致菜单闪退
当父 li 和子 ul 之间有垂直间隙(哪怕只有 1px),鼠标移过去时会经过空白区,:hover 状态丢失,菜单瞬间消失。
- 检查是否设置了
line-height或margin-bottom导致父项底部留空 - 子
ul的top: 100%必须严格对齐,不要用top: 40px这类固定值 - 必要时加
pointer-events: none在父项的伪元素上“桥接”间隙,但更推荐直接消除间隙 - 真正麻烦的不是写不出来,而是这种视觉上看不见的像素级错位,调试时建议打开浏览器开发者工具的“布局叠加”模式



















