多级导航菜单HTML须语义化嵌套<ul>,子菜单作为父<li>的最后一个子节点内嵌;SCSS递归宏需设$depth参数并限制最大6级防爆栈;各层级样式依$depth差异化生成;移动端通过.nav-item--expanded类协同JS控制显隐,不破坏递归结构。
必须采用语义化嵌套 <ul>,并将子菜单作为父项 <li> 的最后一个子节点内嵌,而不能追加在外部。否则 SCSS 无法通过 & > ul 准确识别层级关系。
常见误区是将二级菜单设置为同级 <ul>,结果会造成样式错位或递归失效。正确结构示例:
<ul class="nav"> <li><a href="#">首页</a></li> <li> <a href="#">产品</a> <ul> <!-- 这里开始嵌套 --> <li><a href="#">Web端</a></li> <li> <a href="#">移动端</a> <ul> <!-- 三级 --> <li><a href="#">iOS</a></li> <li><a href="#">Android</a></li> </ul> </li> </ul> </li></ul>
必须明确限制递归深度,默认支持最多 6 级通常已经足够;如果不设置上限,可能导致编译器栈溢出(Sass 会报 Stack level too deep)。实现重点是通过参数控制层级,并在基础层结束递归。
实践建议:
立即学习“前端免费学习笔记(深入)”;
$depth 参数,默认值设为 1
$depth 加 1,当 $depth > 6 时直接 @return 退出& > ul,防止误选深层嵌套中的非直系 ul
@include nav-submenu($depth + 1) 实现下一级调用纯 CSS 没法动态读取嵌套深度,但 SCSS 递归宏可以基于 $depth 值生成不同规则。比如:
display: flex,从二级开始垂直展开left 相应偏移增加 100%(从而实现逐级向右缩进):hover 时才显示(需要配合 position: absolute 和 visibility: hidden)opacity 和 transform: scaleY(0) 实现渐变展开动画,但需要注意 transform-origin: top 才可以从顶部展开递归生成的样式默认覆盖全部视口,但在移动端,多级菜单需要收拢为汉堡菜单并通过点击展开。不能只用 media query 进行简单覆盖,否则递归逻辑会遭到破坏。
正确方案是保留原有递归结构,只调整触发方式与可见性策略:
:hover 显示子菜单;移动端则使用 .nav-item--expanded > ul 类控制显示和隐藏nav-item--expanded 类,不直接修改 DOM 结构.nav-item--expanded > ul 另写一套展开逻辑,使其与递归宏并列,而不嵌套于 @mixin nav-submenu 内ul 依然具备 position: absolute 和 top: 100%,否则点击展开后会出现位置错乱递归只负责生成样式,本身不处理交互;交互响应必须由 JS 配合额外的 class 规则完成,而这一点最容易被忽略。