Next.js App Router中如何检测指向页面内锚点的激活Link?
解决Next.js中页面内锚点激活状态识别问题
Next.js的usePathname仅返回URL的路径部分(比如/),不会包含锚点(hash)信息,所以无法用来判断页面内的激活锚点。以下两种方案可以解决这个问题:
方案1:监听URL hash变化
直接通过window.location.hash获取当前锚点,并监听hashchange事件更新状态,适合仅通过点击导航切换锚点的场景:
import { useState, useEffect } from 'react'; import Link from 'next/link'; const NavBar = () => { const [currentHash, setCurrentHash] = useState(window.location.hash); useEffect(() => { const handleHashChange = () => setCurrentHash(window.location.hash); window.addEventListener('hashchange', handleHashChange); return () => window.removeEventListener('hashchange', handleHashChange); }, []); const isActive = (href) => { // 统一格式:把href的前缀/去掉,和currentHash对比 const targetHash = href.replace(/^\//, ''); return currentHash === targetHash ? "text-orange-500 animation-active" : "text-black-500 hover:text-orange-500"; }; return ( <nav className="flex gap-4"> <Link href="/" className={isActive('/')}>首页</Link> <Link href="/#about" className={isActive('/#about')}>关于</Link> <Link href="/#contact" className={isActive('/#contact')}>联系</Link> </nav> ); }; export default NavBar;
方案2:使用Intersection Observer监听视口区域
如果需要支持用户滚动页面自动激活对应导航项,推荐使用Intersection Observer API,它能监听元素是否进入视口:
import { useState, useEffect } from 'react'; import Link from 'next/link'; const NavBar = () => { const [activeHash, setActiveHash] = useState(''); useEffect(() => { // 页面中所有带id的section(对应锚点) const sections = document.querySelectorAll('section[id]'); if (!sections.length) return; const observer = new IntersectionObserver( (entries) => { entries.forEach(entry => { if (entry.isIntersecting) { setActiveHash(`#${entry.target.id}`); } }); }, { // 调整触发阈值:当元素顶部进入视口10%时激活 rootMargin: '-10% 0px -80% 0px', threshold: 0.1 } ); sections.forEach(section => observer.observe(section)); return () => sections.forEach(section => observer.unobserve(section)); }, []); const isActive = (href) => { const targetHash = href.replace(/^\//, ''); return activeHash === targetHash ? "text-orange-500 animation-active" : "text-black-500 hover:text-orange-500"; }; return ( <nav className="flex gap-4"> <Link href="/" className={isActive('/')}>首页</Link> <Link href="/#about" className={isActive('/#about')}>关于</Link> <Link href="/#contact" className={isActive('/#contact')}>联系</Link> </nav> ); }; export default NavBar;
注意事项
- 方案1仅响应导航点击导致的锚点变化,方案2同时支持滚动触发的激活状态更新,体验更全面。
- 可根据页面布局调整
IntersectionObserver的rootMargin和threshold参数,优化激活触发时机。
内容的提问来源于stack exchange,提问作者bcsta
相关产品推荐
相关产品推荐

