参考答案:
“目录中自动高亮”(TOC Active)本质是 根据当前滚动位置,判断用户正在阅读的标题节点,并同步更新目录状态。这是文档类产品、Markdown 阅读器中非常常见的能力。
下面从 设计思路 → 常见实现方案 → 工程细节与坑 三个层次说明。
滚动驱动状态,状态驱动目录高亮
关键问题只有两个:
1const headings = document.querySelectorAll('h1, h2, h3'); 2 3const observer = new IntersectionObserver( 4 (entries) => { 5 entries.forEach(entry => { 6 if (entry.isIntersecting) { 7 setActiveId(entry.target.id); 8 } 9 }); 10 }, 11 { 12 rootMargin: '0px 0px -70% 0px', 13 threshold: 0 14 } 15); 16 17headings.forEach(h => observer.observe(h));
rootMargin: -70%:让标题进入页面上半部分就触发isIntersecting 表示元素进入“有效阅读区”1const headings = [...document.querySelectorAll('h1,h2,h3')]; 2 3window.addEventListener('scroll', () => { 4 let active = null; 5 6 for (const h of headings) { 7 const { top } = h.getBoundingClientRect(); 8 if (top <= 100) { 9 active = h; 10 } else { 11 break; 12 } 13 } 14 15 if (active) { 16 setActiveId(active.id); 17 } 18});
requestAnimationFrame 节流| 优点 | 缺点 |
|---|---|
| 兼容性好 | 性能较差 |
| 易理解 | 手写逻辑多 |
如果你是:
可以直接从 AST / Node 结构获取标题位置
headingId → offsetTopscrollTop + 二分查找1function scrollToHeading(id) { 2 document.getElementById(id)?.scrollIntoView({ 3 behavior: 'smooth', 4 block: 'start' 5 }); 6}
1window.scrollTo({ 2 top: el.offsetTop - headerHeight, 3 behavior: 'smooth' 4});
规则:
boundingClientRect.top 排序title + index 或 hash| 场景 | 推荐方案 |
|---|---|
| 普通文档 | IntersectionObserver |
| 老浏览器 | scroll + rect |
| 编辑器 | AST + offset |
最近更新时间:2026-02-27

题库维护不易,您的支持就是我们最大的动力!