scroll-behavior: smooth 必须写在 html 元素上才生效,写在 body 或 div 上无效;需确保 href 与 id 严格匹配、目标元素可见且在文档流中,并配合 scroll-margin-top 避开固定头部,同时注意 ios safari 兼容性及系统“减少动画”设置影响。

scroll-behavior: smooth 必须写在 html 上才生效
写在 body 或任意 div 上都不会触发页面级锚点平滑滚动——浏览器的根滚动容器是 html 元素,不是 body。哪怕本地测试“好像动了”,也大概率是 JS 干预或浏览器偶然行为。
正确写法只有一行:html { scroll-behavior: smooth; }
- 不要加
!important,也不要嵌套在媒体查询或 CSS-in-JS 动态规则里 - 避免被第三方样式覆盖:检查开发者工具的 computed 样式,确认
scroll-behavior确实是smooth - 某些重置库(如
modern-normalize)或 UI 框架(如 Ant Design)会悄悄写html, body { scroll-behavior: auto !important; },必须手动覆盖
锚点跳转失效?先查这三件事
即使 html { 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就管不到它
固定头部遮住锚点?用 scroll-margin-top,别算偏移量
浏览器默认把锚点元素顶部对齐视口顶部,position: fixed 或 sticky 导航栏会直接盖住内容。这不是滚动不准,是定位逻辑本身没考虑头部高度。
最稳定解法是给目标元素加 scroll-margin-top:
h2[id] { scroll-margin-top: 60px; }
- 值建议与导航栏实际高度一致;响应式场景可用
clamp(50px, 8vh, 80px) - 不要给
html或body加scroll-padding-top——它控制的是整个视口内边距,和锚点定位无关 - 这个属性和
scroll-behavior是搭档关系,缺一不可
移动端和旧 Safari 别指望它自动工作
scroll-behavior: smooth 在 iOS Safari 上支持极差:15.4 之前完全不支持;15.4–16.0 支持但常卡顿、跳帧甚至静默失败;iOS 16+ 仅对 element.scrollIntoView({ behavior: 'smooth' }) 有效,html 上的声明基本被忽略。
- 安卓 Chrome 89+ 支持尚可,但 Cordova/Capacitor 封装的 WebView 可能拦截 hash 变更,导致 CSS 方案彻底失效
- 用户开启系统「减少动画」(
prefers-reduced-motion: reduce)时,浏览器会静默禁用该属性,不报错也不提示 - 真正可靠的兜底方案不是 JS 滚动库,而是监听
hashchange后调用scrollIntoView({ behavior: 'smooth' }),且需确保目标元素已挂载
平滑滚动不是魔法,是滚动上下文、目标可见性、系统偏好和浏览器实现的共同结果。最容易被忽略的,其实是目标元素是否真正在文档流中、是否被隐藏、以及移动端 Safari 的实际表现——这些地方一错,CSS 再正确也没机会执行。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











