scroll-behavior: smooth 必须写在 html 元素上才生效,因浏览器根滚动容器是 html;仅对原生锚点跳转和未指定 behavior 的 scrollintoview() 有效,ios safari 旧版本及启用了 prefers-reduced-motion 时会静默失效。

scroll-behavior: smooth 必须写在 html 元素上才生效,其他地方基本白搭。
为什么只写 html { scroll-behavior: smooth; } 才管用
浏览器真正的根滚动容器是 html,不是 body,也不是某个 .container。哪怕你看到滚动条在窗口右边,底层滚动上下文也绑定在 html 上。Safari 尤其严格,body { scroll-behavior: smooth; } 会被直接忽略。
检查是否生效,别只看 CSS 文件,要在开发者工具的「Computed」面板里确认 html 的 scroll-behavior 值确实是 smooth —— 很多 UI 框架(比如 Ant Design)或重置样式(如 * { scroll-behavior: auto !important; })会悄悄把它打回 auto。
-
body { margin: 0; }建议加上,防止某些浏览器因默认外边距干扰根滚动行为 - 如果用了局部滚动容器(比如
div.content { overflow-y: auto; }),那得单独给它加scroll-behavior: smooth,和html无关 -
* { scroll-behavior: smooth; }无效:该属性不继承,且会被更具体的选择器覆盖
scroll-behavior: smooth 能触发哪些操作
它只对两类行为起效:用户点击原生锚点链接(a[href="#section"]),以及 JS 调用 element.scrollIntoView() 且未传 behavior 参数时的默认行为。
-
element.scrollTop = 100、window.scrollTo(0, 200)—— 完全不受影响,硬跳 - 想用 JS 平滑滚动,必须显式传参:
element.scrollIntoView({ behavior: 'smooth' })或window.scrollTo({ top: 100, behavior: 'smooth' }) - Vue/React 路由跳转(如
router.push('/about#contact'))不会触发,因为不是原生锚点导航,需手动调用滚动 API
目标被 fixed 导航栏遮住?别手算 offset
锚点跳转后内容被盖住,不是滚动不准,是浏览器默认把目标顶部对齐视口顶部。手动减去 offsetTop - navbarHeight 会破坏惯性、回弹和 scroll 事件时机。
- 给目标元素加
scroll-margin-top,比如h2[id] { scroll-margin-top: 60px; } - 值建议等于导航栏实际高度;响应式场景可用
scroll-margin-top: clamp(50px, 8vh, 80px); -
scroll-margin-top只对锚点跳转和scrollIntoView()生效,对scrollTo()无效 - 不要给
html或body加scroll-padding-top,它控制整个视口内边距,和锚点定位无关
iOS Safari 和系统偏好会静默禁用它
scroll-behavior: smooth 在 iOS Safari ≤15.3 完全无效;15.4–16.0 支持但极不稳定;微信 X5 WebView 常静默降级为瞬时滚动。更隐蔽的是:@media (prefers-reduced-motion: reduce) 开启时,所有平滑行为会被强制禁用,且不报错、不警告、不 fallback。
- 必须用 JS 主动检测:
typeof Element.prototype.scrollIntoView === 'function' - 监听
scroll事件是否连续触发(瞬时滚动只触发一次) - 不能只靠 CSS,移动端必须准备 JS 回退方案
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











