多级下拉菜单应使用独立的 dropdown block 而非嵌套命名,如 nav__item__submenu;每层均为可复用、可独立测试的 block,通过 modifier(如 dropdown--nested)和 DOM 层级控制样式与行为。

多级下拉菜单不该用 nav__item__submenu 这类嵌套命名
这种写法看似“表达了层级”,实则破坏 BEM 的模块边界。nav__item__submenu 暗示 submenu 是 nav__item 的子元素,但语义上它是个可独立显隐、带定位逻辑、甚至可能复用到侧边栏的交互单元。一旦你给二级菜单加个 dropdown--slide 动效,就不得不写成 nav__item__submenu--slide——这等于把 dropdown 的行为逻辑硬塞进 nav 的命名空间里。
真正该做的,是承认:每一层下拉都是一个独立 dropdown block。一级菜单项里放一个 dropdown,它的面板里某个 dropdown__item 又可以再放一个 dropdown 实例。BEM 不靠类名深度表达嵌套,而靠 DOM 层级 + 独立 block 复用实现递归。
- 禁止出现
nav__item__dropdown、dropdown__menu__submenu、nav__dropdown--level-2 - 允许且推荐:
<div class="dropdown"></div>在任意位置重复使用,包括嵌套在另一个dropdown__menu内部 - 修饰符用于变体控制,如
dropdown--hover(悬停触发)或dropdown--click(点击触发),而非层级标识
hover 闪退?大概率是 .dropdown__toggle:hover + .dropdown__menu 断链了
CSS 兄弟选择器 .dropdown__toggle:hover + .dropdown__menu 要求两个元素必须是**紧邻兄弟节点**。只要中间插了一个无关 wrapper(比如 Vue 的 <transition></transition>、React 的 <fragment></fragment>、或手写的 <div class="wrapper">),这个选择器就完全失效——不是动画没写对,是结构没对齐 BEM 的前提假设。
<ul>
<li>正确 HTML 结构必须是:<code><button class="dropdown__toggle"></button><div class="dropdown__menu"></div>(无间隔)
.dropdown:hover .dropdown__menu,但需确保 .dropdown 有 position: relative 以约束绝对定位的 menu:focus-within 替代 hover,避免纯 hover 在触屏设备上不可用子 dropdown 的样式怎么和父级区分?别靠前缀,用 modifier + DOM 层级
你想让二级菜单缩进 20px、z-index 高一点、箭头方向朝右——这些不是“它属于第二层”导致的,而是“它出现在子级上下文”带来的表现差异。BEM 不鼓励用父级上下文去命名子元素(如 dropdown--in-nav-item),而应通过 modifier 表达自身状态,并利用 CSS 层级关系控制继承。
立即学习“前端免费学习笔记(深入)”;
- 给子 dropdown 加
dropdown--nested修饰符,定义基础缩进、过渡延迟等共性 - 用
.dropdown__menu .dropdown--nested .dropdown__menu这类后代选择器微调嵌套深度的表现,而不是靠类名拼接 - 避免
dropdown__menu--secondary:menu 本身不是 block,不能加 modifier;要改的是整个dropdown实例的状态
为什么 comment--nested 可行,但 nav__item__dropdown 不行?关键在“是否可复用”
comment--nested 合理,是因为所有评论(无论嵌套几层)都共享同一套语义:作者、内容、时间、回复按钮——它们本质是同一 block 的不同实例。而 nav__item__dropdown 错在把 dropdown 当作 nav 的附属品,忽略了它在搜索框、用户头像菜单、甚至表单内也能复用的事实。
真正容易被忽略的点是:BEM 的 block 划分不是看视觉位置,而是看**职责是否可剥离、是否能脱离当前上下文独立存在并测试**。一个 dropdown 组件,应该能单独跑 demo 页面、能被 Storybook 加载、能接受 props 控制展开逻辑——如果它只能活在 nav__item 里,那它就不是 block,只是 nav 的一个私有实现细节。


















