方向我完全同意,但「IO 判可见比例最高」这个判据本身在滚动中会抖,我一般不用它。
判定带比比例更稳:IntersectionObserver 的 threshold 只在越界时回调,滚动过程里拿不到实时的「谁占比最高」,而且首节/末节天生吃亏——滚到底时最后一节可能只占视口 30%,高亮就永远跳不过去。直接压一条判定带更省事:rootMargin: '-80px 0px -60% 0px'(上边避顶栏,下边砍掉 60%),谁进入这条带谁高亮,不用算比例。末节到不了位就在滚动到底时强制点亮最后一个,一行判断的事。
scrollend 有个死角:点当前已在位置的那一节时,scrollTo 的目标和当前完全一致,浏览器压根不产生滚动,scrollend 不会触发,isAnimating 就永久卡 true,高亮从此死掉。稳妥做法是 scrollTo 之前先比对 Math.abs(currentTop - targetTop) < 2 就直接跳过不清标志位。另外打断场景下,滚动停止本身通常也会触发一次 scrollend,所以你补的 wheel/touchstart 清理更多是给老浏览器的超时兜底路径用的,两条都留着不冲突,只是别指望它俩是主路径。
container.scrollTo({ top: el.offsetTop }) 的隐含前提是容器的定位。容器一旦是 static,el.offsetTop 是相对外面那个定位祖先算的,数会直接飘到姥姥家——这也是为什么「容器 position: relative」这个前提必须写进注释。想稳一点就判一下 el.offsetParent === container,不等就退回 rect 那套。
scrollRestoration = 'manual' 记得配套做列表页位置恢复,否则后退回去用户在长列表里直接懵,等于把坑从详情页挪到了列表页。
见习用户






