评分组件中,.rating 是 Block,.rating__star 是 Element,状态用语义化 Modifier 如 --selected;禁用应作用于 Block;“滑过高亮”需 JS 计算位置并动态添加 --hovered 类。

评分组件的 Block 和 Element 怎么定
评分组件本质是一个独立可复用的功能单元,不是视觉分组。Block 名必须表达功能,比如 .rating,而不是 .star-group 或 .section-rating。内部星星是它的组成部分,不能脱离 .rating 单独存在,所以统一用 .rating__star 表示每个可交互星形元素。
-
.rating是 Block,负责整体容器、布局和基础交互逻辑 -
.rating__star是 Element,只承担“单颗星”的渲染与状态响应 - 不要写
.rating<strong>star</strong>icon——BEM 禁止三层嵌套;图标应作为.rating<strong>star</strong>的子元素(如<svg></svg>),样式通过.ratingstar::before或直接设宽高控制
交互状态该用 Modifier 还是 class 切换
所有交互状态(hover、focus、选中、禁用)都必须走 Modifier,不能靠 JS 直接加 .is-active 或 .disabled 这类通用类名。
- 正确:
.rating<strong>star--selected</strong>、.ratingstar--hovered、.rating--readonly - 错误:
.rating<strong>star.active</strong>、.ratingstar[disabled](后者虽合法但破坏 BEM 状态一致性)
Modifier 必须语义化、可预测:
-
--selected表示当前被用户点中(非仅视觉高亮) -
--hovered仅用于悬停反馈,不改变实际值 -
--readonly修饰整个 Block,表示整组不可操作,此时所有.rating__star应忽略 pointer-events
如何用 CSS 实现“鼠标滑过时高亮到当前位置”的效果
这个行为依赖两个关键点:父容器监听 hover + 子元素根据位置响应。不能只靠 .rating__star:hover,因为那只能高亮单颗星。
- 给
.rating加position: relative,确保子元素定位可控 - 所有
.rating__star设为 inline-block 或 flex item,保持顺序流式排列 - 使用
.rating:hover .rating__star配合属性选择器或伪类(如:nth-of-type(n))实现“滑过第 n 颗星时,前 n 颗都带--hovered”——但这得靠 JS 控制类名,CSS 无法纯靠选择器推导“前面几颗”
实操建议:
- 用 JS 在
.rating上监听mousemove,计算鼠标相对于容器的横向位置,再根据星数算出应高亮数量 - 动态给对应数量的
.rating<strong>star</strong>添加.ratingstar--hovered类 - 注意防抖,避免频繁重绘;移动端需 fallback 到
touchstart
disabled 状态为什么不能只加在单颗星上
.rating__star--disabled 是反模式。评分组件的禁用是整体行为:用户不该能点、不该响应 hover、不该暴露键盘焦点。
- 正确做法是给 Block 加
.rating--disabled,然后在 CSS 中统一压制:.rating--disabled { pointer-events: none; opacity: 0.5; } .rating--disabled .rating__star { cursor: not-allowed; } - 如果只禁用某几颗星(比如灰掉已评过的),那是业务逻辑,应使用
.rating__star--locked这类语义化 Modifier,而非--disabled - 浏览器原生
disabled属性对<div> 无效,对 <code><button></button>有效但会丢失样式继承链,所以统一走 CSS Modifier 更可控BEM 的关键不在写法多规范,而在每次加类名前问一句:“这个变化,是 Block 的变体?Element 的变体?还是跨层级的状态泄露?”——漏掉这句,
--hovered就容易变成--v2,.rating就悄悄耦合了页面结构。











