如何在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
相关产品推荐
相关产品推荐

