You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 05:07:12