html { scroll-behavior: smooth; } 必须写在 html 元素上才可靠生效,因页面级滚动上下文绑定于 html;body 上声明在 safari ≤15.4 中无效,chrome/firefox 也不稳定;仅对锚点链接和 scrollintoview()(未传 behavior 时默认或显式设为 'smooth')生效,window.scrollto() 等需显式指定 behavior: 'smooth';锚点被固定头遮挡时用 scroll-margin-top 补偿;ios safari 15.4 前不支持,webview 常静默降级;需运行时检测 scrollintoview 可用性,并尊重 prefers-reduced-motion。

直接写 html { scroll-behavior: smooth; } 就能生效,但 90% 的“没反应”是因为写错了位置、目标环境不支持,或被其他样式/行为覆盖。
为什么写在 body 上基本无效
浏览器真正的页面级滚动容器是 html 元素,不是 body。哪怕你看到滚动条在窗口右侧、body 高度撑满,底层滚动上下文仍绑定在 html 上。Safari(尤其是 ≤15.4)会直接忽略 body { scroll-behavior: smooth; },Chrome/Firefox 可能“碰巧”渲染,但不可靠。
- 务必删掉所有
body或通配符(如* { scroll-behavior: smooth; })上的声明 - 在开发者工具的 Computed 样式中检查
html元素,确认scroll-behavior值确实是smooth,而不是auto或被!important覆盖 - 某些 CSS 重置(如
* { margin: 0; })或 Normalize.css 可能把html的overflow设为hidden,导致该属性静默失效
哪些操作能触发平滑,哪些不能
scroll-behavior: smooth 是声明式兜底,只对两类原生行为生效:用户点击锚点链接(@#@#@#@#@#@#@#@#@#@0;el.scrollIntoView({ behavior: 'smooth' })
window.scrollTo(0, 200)、el.scrollTop = 100、Vue/React 路由跳转(如 router.push('/page#target'))window.scrollTo({ top: 200, behavior: 'smooth' }),旧写法不触发平滑scrollIntoView,目标元素可能尚未挂载,建议包一层 requestAnimationFrame
锚点跳转后内容被固定头部遮挡怎么办
这不是滚动不准,而是浏览器默认将目标元素顶部对齐视口顶部,而 position: fixed 或 sticky 导航栏盖住了内容。
- 给目标元素加
scroll-margin-top: 60px(值等于导航栏高度),浏览器会在平滑滚动结束时自动预留空白 - 该属性只对锚点跳转和
scrollIntoView()生效,对scrollTo()无效 - 避免用
%单位——计算时机不稳定,尤其在 resize 或字体缩放后容易错位 - 若导航栏高度动态变化(如折叠/展开),纯 CSS 难覆盖,应改用
el.scrollIntoView({ block: 'start', behavior: 'smooth' })+padding-top补偿
iOS Safari 和 WebView 的兼容性陷阱
移动端仅靠 scroll-behavior: smooth 基本不可靠:iOS Safari 15.4 前完全不支持,微信 X5 等 WebView 常静默降级为瞬时滚动。
- 真实检测不能只看
@supports (scroll-behavior: smooth)——它只测 CSS 解析能力,不反映 JS 行为是否可用 - 运行时检测应为:
typeof Element.prototype.scrollIntoView === 'function'+ 实际调用后监听scroll事件是否延迟、连续触发 - 用户开启系统“减少动画”偏好(
prefers-reduced-motion: reduce)时,所有平滑行为会被浏览器强制禁用,且不报错 - 局部滚动容器(如
.chat-list { overflow-y: auto; })必须自己加scroll-behavior: smooth,不能指望html的声明“穿透”过去
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











