导航高亮跟随
导航高亮跟随:滚动到某节时,导航对应项自动高亮——常见于文档站与单页应用(SPA)。同样用 IntersectionObserver:观察每个内容区,谁在视口里就高亮谁的导航项。
踩坑点:监听 scroll 计算 getBoundingClientRect 性能差且抖动;正确做法是 IO + rootMargin 微调(如顶部预留 -40% 让高亮提前一点),并用一个观察器批量管理。
- 每个内容区一个 id,导航项 href="#id" 对应
- IntersectionObserver + rootMargin 控制高亮时机
- 滚到页面底部时强制高亮最后一项(边界处理)
HTML
<style> #sp { display: flex; height: 200px; } #spnav { width: 110px; display: flex; flex-direction: column; gap: 4px; } #spnav a { padding: 8px 10px; border-radius: 6px; color: #666; text-decoration: none; font-size: 13px; transition: .2s; } #spnav a.on { background: #d2a8ff; color: #fff; } #spbody { flex: 1; overflow: auto; padding: 0 16px; } #spbody section { height: 200px; padding: 16px; box-sizing: border-box; } </style> <div id="sp"> <nav id="spnav"> <a href="#" data-s="s1">第一节</a><a href="#" data-s="s2">第二节</a> <a href="#" data-s="s3">第三节</a> </nav> <div id="spbody"> <section id="s1">第一节内容</section> <section id="s2">第二节内容</section> <section id="s3">第三节内容</section> </div> </div> <script> var body = document.getElementById('spbody'); var links = document.querySelectorAll('#spnav a'); var map = {}; links.forEach(function (a) { map[a.dataset.s] = a; }); document.querySelectorAll('#spbody section').forEach(function (sec) { new IntersectionObserver(function (es) { es.forEach(function (e) { if (e.isIntersecting) { links.forEach(function (a) { a.classList.remove('on'); }); map[e.target.id].classList.add('on'); } }); }, { root: body, rootMargin: '-20% 0px -70% 0px' }).observe(sec); }); links.forEach(function (a) { a.addEventListener('click', function (e) { e.preventDefault(); document.getElementById(a.dataset.s).scrollIntoView({ behavior: 'smooth' }); }); }); </script>
本节要点滚动高亮 = IntersectionObserver + rootMargin 微调;点击导航用 scrollIntoView 平滑滚。




