React导航栏路由下划线窗口Resize时偏移问题求解
解决React导航栏下划线窗口resize时错位的问题
方案一:纯CSS实现(推荐,简洁高效)
无需单独维护下划线组件,通过给当前激活的导航链接添加active类,用CSS伪元素实现下划线,自动适配窗口resize。
步骤1:修改导航链接的渲染
给匹配当前路由的链接添加active类:
import { useLocation } from "react-router-dom"; function Navbar() { const { pathname } = useLocation(); return ( <nav style={{ position: "relative", padding: "1rem 0" }}> <a href="/home" className={pathname === "/home" ? "nav-link active" : "nav-link"} id="home" > 首页 </a> <a href="/about" className={pathname === "/about" ? "nav-link active" : "nav-link"} > 关于 </a> <a href="/contact" className={pathname === "/contact" ? "nav-link active" : "nav-link"} > 联系我们 </a> </nav> ); }
步骤2:添加CSS样式
用::after伪元素生成下划线,自动跟随链接的位置和宽度:
.nav-link { position: relative; margin: 0 1rem; text-decoration: none; color: #333; } .nav-link.active::after { content: ""; position: absolute; bottom: -5px; /* 对应原下划线的margin-bottom */ left: 0; width: 100%; height: 4px; background: linear-gradient(to bottom, #48a0e0 20%, #2b58a5); box-shadow: 0 -14px 14px #48a0e0; transition: all 0.3s ease-in-out; }
优势:无需JS计算位置和宽度,窗口resize时自动适配,代码更简洁,性能更优。
方案二:优化原JS组件
如果必须保留独立的下划线组件,修复resize监听逻辑,优化性能和定位准确性:
修改后的Underline组件
import { useEffect, useState, useRef } from "react" import { useLocation } from "react-router-dom" export default function Underline() { const { pathname } = useLocation() const [underlineStyle, setUnderlineStyle] = useState({}) // 导航栏容器的ref,用于计算相对定位偏移 const navContainerRef = useRef(null) // 提取更新下划线位置的公共函数 const updateUnderlinePosition = () => { let targetLink = document.querySelector(`a[href="${pathname}"]`) // 处理home链接的特殊逻辑 if (targetLink?.getAttribute("id") !== null) { targetLink = document.querySelector("#home") } if (!targetLink) return // 计算相对于导航栏容器的left偏移(避免视口滚动影响) const containerRect = navContainerRef.current?.getBoundingClientRect() || { left: 0 } const linkRect = targetLink.getBoundingClientRect() setUnderlineStyle({ width: `${targetLink.offsetWidth}px`, left: `${linkRect.left - containerRect.left}px`, }) } useEffect(() => { // 初始化时更新位置 updateUnderlinePosition() // 用requestAnimationFrame节流resize事件,避免频繁触发 let resizeFrameId const handleResize = () => { cancelAnimationFrame(resizeFrameId) resizeFrameId = requestAnimationFrame(updateUnderlinePosition) } // 正确绑定resize监听(传函数引用,而非立即执行) window.addEventListener("resize", handleResize) return () => { window.removeEventListener("resize", handleResize) cancelAnimationFrame(resizeFrameId) } }, [pathname]) // 需要将navContainerRef绑定到导航栏的父容器元素上 return ( <> {/* 假设导航栏容器是这个div,或者将ref传递给已有的导航容器 */} <div ref={navContainerRef} style={{ position: "relative" }}> {/* 导航链接放在这里 */} </div> <div id="underline" style={underlineStyle}></div> </> ) }
关键修复点:
- 修复resize事件绑定:传递函数引用
handleResize而非立即执行handleResize() - 用
requestAnimationFrame节流resize事件,平衡响应速度和性能 - 计算相对于导航栏容器的偏移量,避免视口滚动或resize时的定位偏差
- 提取公共更新函数,在路由切换和resize时复用
内容的提问来源于stack exchange,提问作者Finlay Foulds
相关产品推荐
相关产品推荐

