纯CSS无法真正实现下划线跟随鼠标滑动,因:hover仅作用于单元素且CSS无运行时坐标读取能力,所有“跟随”效果实为各链接独立触发::after动画,存在消失再出现的视觉断层。

为什么纯 CSS 无法真正“跟随”鼠标滑动
所谓“下划线跟随”,常被误解为一条线能平滑地从一个导航项底部滑到另一个——但 :hover 是单元素状态,CSS 没有运行时坐标读取能力,getBoundingClientRect() 这类 API 只能在 JS 中调用。所有标榜“纯 CSS 跟随”的方案,实际都是每个 a 独立触发自己的 ::after 动画,中间必然存在断层:前一个消失、后一个才出现,视觉上就是“跳”或“闪”。
用 ::after + transform: scaleX() 实现最稳的 hover 展开效果
这是目前兼容性最好、性能最高、无需 JS 的方案,适合绝大多数产品级导航栏。
- 父容器(如
nav或.nav)必须设overflow: hidden,否则scaleX(0)缩放时伪元素会溢出 - 每个导航项(如
a)需设position: relative,为伪元素提供定位上下文 -
a::after必须是position: absolute、bottom: 0、width: 100%、height: 2px - 动画关键:默认不写
transform: scaleX(0),只在a:hover::after中设transform: scaleX(1),并把transition: transform 0.3s ease写在默认伪元素上 -
transform-origin: center bottom必须显式声明,否则下划线会从左边缘拉伸,不是居中生长
想“伪跟随”?用 CSS 变量 + translateX() 预设位置
如果导航项宽度固定或可预估(比如 5 个等宽菜单),可用 --underline-left 变量配合 transform: translateX() 模拟滑动感。但它仍是“切换”,不是实时跟随。
- 给每个导航项手动设变量:
.nav-item:nth-child(1) { --underline-left: 0; }、.nav-item:nth-child(2) { --underline-left: 120px; }(需按实际像素计算) - 共享的
.nav::after用transform: translateX(calc(var(--underline-left, 0) - 50%))居中对齐(减去自身宽度一半) - 必须用
transform而非left,避免重排;transition也必须写在.nav::after上,不能写在 hover 触发里 - 该方案在 Safari 旧版或 flex 容器中易因小数像素偏移,建议加
Math.round()逻辑(需 JS 初始化)——此时已不算“纯 CSS”
移动端点击区域和 :hover 失效问题
iOS Safari 对纯文字链接的 :hover 响应极不可靠,尤其未触发 touchstart 前。这不是动画写得不好,而是平台限制。
立即学习“前端免费学习笔记(深入)”;
- 导航项必须加
role="button"和tabindex="0",让 Safari 认为它是可聚焦元素 - 不能只依赖
:hover,要补:focus和:active样式,支持键盘导航 - 真机测试时,若下划线不出现,先检查是否点了空白处没触发,再确认是否用了
cursor: pointer或touch-action: manipulation - 更稳妥的做法是:桌面端用 CSS hover,移动端用 JS 监听
touchstart+click,动态更新 class 或 CSS 变量
真正难的不是写出动画,而是判断什么时候该放弃“纯 CSS 跟随”幻觉——当设计要求无缝滑动、动态宽度、多端一致时,JS 获取 getBoundingClientRect() 并用 requestAnimationFrame 更新 transform 才是唯一可靠路径。CSS 变量方案看似折中,但维护成本和边界 case(比如字体加载延迟导致宽度变化)往往被低估。


















