必须将 scroll-behavior: smooth 写在 html 元素上才生效,因浏览器根滚动容器是 html;仅对原生锚点跳转和 scrollIntoView() 有效,需配合 scroll-margin-top 解决固定头部遮挡,并注意 iOS Safari ≤15.4 不支持及系统“减少动画”偏好会静默禁用。

只有一行 CSS 能真正生效: html { scroll-behavior: smooth; }。写在 body、div 或通配符上,90% 场景下都白搭。
scroll-behavior 必须作用于 html 元素
浏览器真正的滚动容器是 html 元素,不是 body,也不是某个滚动区域的父容器。规范明确要求该属性必须设在根滚动容器上。
-
body { scroll-behavior: smooth; }在 Safari 和旧版 Chrome 中完全不触发平滑行为 -
* { scroll-behavior: smooth; }无效——该属性不继承,且会被后续更具体的选择器覆盖 - 检查开发者工具「Computed」面板,确认
html的scroll-behavior值确实是smooth,而非被modern-normalize或 Ant Design 等框架悄悄重置为auto - 避免
* { scroll-behavior: auto !important; }这类全局重置,它会直接干掉你的设置
锚点跳转仍“瞬移”?大概率是链路断了
scroll-behavior: smooth 不是魔法开关,它只在原生锚点跳转链路完整时才起效。失效几乎从不因为 CSS 写错,而是目标找不到或算不准。
-
href和目标id必须严格一致:大小写、连字符、下划线都不能差,#contact-us对应id="contact-us"可以,#contact_us就不行 - 目标元素不能被隐藏:
display: none、visibility: hidden、父级overflow: hidden或transform都会让浏览器无法获取其位置 - 目标必须在默认文档流中:如果用了
position: absolute脱离布局,或被包在overflow: auto的局部滚动容器里,html上的scroll-behavior就管不到它 - 某些 SPA 路由容器用了
height: 100vh; overflow: hidden布局,导致html实际不可滚动,属性自然不触发
固定导航栏遮住内容?用 scroll-margin-top,别手算偏移
滚动后内容被 position: fixed 或 sticky 导航栏盖住,不是滚动不准,是浏览器默认把目标顶部对齐视口顶部——这个逻辑本身没考虑头部高度。
立即学习“前端免费学习笔记(深入)”;
- 给目标元素加
scroll-margin-top,让滚动终点自动上移:h2[id] { scroll-margin-top: 60px; } - 值建议与导航栏实际高度一致;响应式场景可用
scroll-margin-top: clamp(50px, 8vh, 80px); - 不要给
html或body加scroll-padding-top——它控制整个视口内边距,和锚点定位无关 -
scroll-margin-top和scroll-behavior是搭档关系,缺一不可
iOS Safari 和系统“减少动画”设置会静默禁用它
scroll-behavior: smooth 在 iOS Safari 上支持极差:Safari ≤ 15.4 基本不可靠,即使 16+ 版本也会受系统「减少动画」偏好影响而静默退化为 auto。
- 无法通过 CSS 检测该限制,也无法强制开启
- 若需强一致性体验,得用 JS 补偿:监听
hashchange,调用el.scrollIntoView({ behavior: 'smooth' }) - 注意:JS 手动滚动(如
window.scrollTo())不受scroll-behavior影响,必须显式传{ behavior: 'smooth' }
最常被忽略的其实是「目标是否真在文档流里」——很多 SPA 页面用 display: none 切换路由、用 transform 做过渡动画,这些都会让锚点目标在滚动瞬间“消失”,scroll-behavior 就彻底失能。


















