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

如何在Next.js中使用IntersectionObserver高亮目录(TOC)

实现Next.js中目录条目随内容可见高亮的方案

一、自定义基于IntersectionObserver的React Hook

直接把原生IntersectionObserver逻辑封装成React Hook,适配Next.js场景:

import { useEffect, useRef, useState } from 'react';

export function useActiveSection(sectionIds) {
  const [activeId, setActiveId] = useState('');
  const observers = useRef([]);

  useEffect(() => {
    // 清理旧的观察者实例
    observers.current.forEach(observer => observer.disconnect());
    observers.current = [];

    const observerOptions = {
      root: null,
      rootMargin: '-20% 0px -70% 0px', // 可调整该值控制触发高亮的视口位置
      threshold: 0
    };

    const handleIntersect = (entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          setActiveId(entry.target.id);
        }
      });
    };

    sectionIds.forEach(id => {
      const element = document.getElementById(id);
      if (!element) return;
      
      const observer = new IntersectionObserver(handleIntersect, observerOptions);
      observer.observe(element);
      observers.current.push(observer);
    });

    return () => {
      observers.current.forEach(observer => observer.disconnect());
    };
  }, [sectionIds]);

  return activeId;
}

在页面组件中使用:

import { useActiveSection } from './hooks/useActiveSection';

export default function PostPage() {
  const sectionIds = ['section-1', 'section-2', 'section-3'];
  const activeId = useActiveSection(sectionIds);

  return (
    <div className="flex">
      {/* 目录区域 */}
      <nav className="toc">
        {sectionIds.map(id => (
          <a
            key={id}
            href={`#${id}`}
            className={`toc-item ${activeId === id ? 'active' : ''}`}
          >
            {id === 'section-1' ? '第一章' : id === 'section-2' ? '第二章' : '第三章'}
          </a>
        ))}
      </nav>

      {/* 内容区域 */}
      <main className="content">
        <section id="section-1" className="section">
          <h2>第一章</h2>
          {/* 章节内容 */}
        </section>
        <section id="section-2" className="section">
          <h2>第二章</h2>
          {/* 章节内容 */}
        </section>
        <section id="section-3" className="section">
          <h2>第三章</h2>
          {/* 章节内容 */}
        </section>
      </main>
    </div>
  );
}

搭配高亮样式:

.toc-item.active {
  color: #2563eb;
  font-weight: 600;
  border-left: 3px solid #2563eb;
  padding-left: 8px;
}

二、现成的React第三方库

如果不想手动实现,这些成熟库可以直接用:

  • react-scrollspy:专注滚动监听高亮的库,适配React/Next.js,配置简单
    使用示例:
    import Scrollspy from 'react-scrollspy';
    
    function TOC() {
      return (
        <Scrollspy items={['section-1', 'section-2', 'section-3']} currentClassName="active">
          <li><a href="#section-1">第一章</a></li>
          <li><a href="#section-2">第二章</a></li>
          <li><a href="#section-3">第三章</a></li>
        </Scrollspy>
      );
    }
    
  • react-intersection-observer:封装IntersectionObserver的React工具库,灵活性高,可用于多种监听场景
    示例:
    import { useInView } from 'react-intersection-observer';
    import { useEffect } from 'react';
    
    function Section({ id, title, onSectionActive }) {
      const { ref, inView } = useInView({
        threshold: 0.2,
        rootMargin: '-20% 0px -70% 0px'
      });
    
      useEffect(() => {
        if (inView) {
          onSectionActive(id);
        }
      }, [inView, id]);
    
      return (
        <section ref={ref} id={id}>
          <h2>{title}</h2>
        </section>
      );
    }
    

使用第三方库前,需先通过npm/yarn安装对应包,比如npm install react-scrollspy或npm install react-intersection-observer。

内容的提问来源于stack exchange,提问作者Beres

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 04:30:10