必须调用 event.preventDefault() 阻止原生锚点跳转,再用 scrollIntoView 或 scrollTo 实现平滑滚动;目标存在时用 scrollIntoView,回顶部用 scrollTo;需动态计算导航栏偏移量并兼容 Safari ≤15.3。
根本原因几乎总是 event.preventDefault() 没调用。浏览器看到 href="#about" 会立刻执行原生锚点跳转:URL 变 hash、页面闪动、滚动无动画——这一步不可逆,JS 后续的 scrollIntoView 或 scrollTo 就算执行了,用户也已感知到“卡顿”。
a[href^="#"] 点击事件里加 event.preventDefault()
href 不触发原生跳转,那 scroll-behavior: smooth 就完全不生效,必须 JS 主动滚动scrollIntoView 要求目标元素真实存在且有 id;scrollTo 更适合回到顶部这种无 DOM 目标的场景。
#about:用 document.querySelector("#about")?.scrollIntoView({ behavior: "smooth", block: "start" })
window.scrollTo({ top: 0, behavior: "smooth" }),别写 href="#top",否则先闪再滚if (el) { el.scrollIntoView(...) } 判断,避免报错 Cannot read property 'scrollIntoView' of null
导航栏高度随屏幕尺寸变化(移动端可能折叠、PC端固定 80px),硬编码 -80 会导致部分设备内容被盖住或留白过多。
const navbar = document.querySelector(".fixed-top"); const offset = navbar ? navbar.offsetHeight : 0;
target.offsetTop - offset
scrollIntoView,它不支持直接传偏移量,得退到 scrollTo:window.scrollTo({ top: target.offsetTop - offset, behavior: "smooth" })
scroll-behavior: smooth 无法配合 offset 补偿,纯 CSS 方案在这里失效IE 已淘汰,连 scrollIntoView 都不支持,更别说 behavior 参数——直接忽略即可。真正要兼容的是 Safari ≤15.3。
smoothscroll-polyfill(仅 2KB),它会自动 patch scrollTo 和 scrollIntoView,业务代码不用改window.scrollTo({ top: y }) + CSS scroll-behavior: smooth 组合,但后者只对原生锚点生效,且仍需 preventDefault 配合实际滚动触发前,DOM 必须就位。如果目标区块是异步加载或条件渲染的(比如 tab 切换后才显示的 section),querySelector 会返回 null,滚动就静默失败——这点最容易被忽略。