Back

使用 CSS scroll-behavior 实现平滑滚动

使用 CSS scroll-behavior 实现平滑滚动

点击锚点链接后,页面瞬间跳转到某个区域,这种体验显得很突兀。它会让用户感到迷失方向,打断阅读节奏,使页内导航显得不够精致。解决方案其实只需一个 CSS 属性——无需任何 JavaScript 库。

核心要点

  • scroll-behavior: smooth CSS 属性可为锚点链接和编程式滚动 API 启用动画式滚动,无需 JavaScript。
  • 应将其应用于 html 元素,而非 body,以便该属性正确地传播到视口。
  • 浏览器控制的缓动函数和动画时长无法通过 CSS 自定义——当需要精细控制时再考虑 JavaScript。
  • 使用 scroll-margin-top 防止固定头部遮挡锚点目标。
  • 将规则包裹在 prefers-reduced-motion: no-preference 媒体查询中,以尊重用户的无障碍偏好。

什么是 scroll-behavior?它如何工作?

scroll-behavior CSS 属性 控制滚动容器在通过编程方式触发滚动时的移动方式——例如锚点链接、哈希导航或 JavaScript 滚动 API(如 window.scrollTo()element.scrollIntoView())。

它接受两个值:

  • auto — 默认值。瞬时滚动,无动画效果。
  • smooth — 使用浏览器定义的缓动函数和时长以动画方式滚动。

需要特别说明的一点:scroll-behavior 不会影响用户通过鼠标滚轮、触控板或拖动滚动条触发的滚动。它仅作用于锚点触发和编程式滚动。

缓动函数和动画时长完全由浏览器控制。仅靠 CSS 无法自定义它们。如果你需要特定的时长或自定义缓动曲线,则需要基于 JavaScript 的解决方案。

为页面添加 CSS 平滑滚动

scroll-behavior: smooth 应用于 html 元素,即可为整个页面启用平滑滚动导航:

html {
  scroll-behavior: smooth;
}

使用 html,而非 body 当设置在根元素上时,该属性会应用于视口。设置在 body 上则不会传播到视口——这是平滑滚动失效时常见的困惑来源。

这一条声明就能自动处理所有锚点链接的滚动。无需 JavaScript,无需依赖。

实际应用场景

目录导航:

<nav>
  <a href="#intro">Introduction</a>
  <a href="#usage">Usage</a>
  <a href="#examples">Examples</a>
</nav>

返回顶部按钮:

<a href="#top">↑ Back to top</a>

要让”返回顶部”链接精准定位到页面最顶部,请确保页面起始位置存在一个 id="top" 的元素,或使用 href="#" 作为后备方案。一旦在 html 上设置了 scroll-behavior: smooth,这两种写法都能立即生效。

使用 scroll-margin-top 处理固定头部

如果布局中包含固定头部,锚点目标会被部分遮挡在头部之下。可以使用 scroll-margin-top 解决这个问题:

:target {
  scroll-margin-top: 80px; /* match your header height */
}

为了覆盖更广泛的场景——包括以编程方式滚动到非当前 :target 的元素——可以将该规则直接应用于 section 元素本身:

section[id] {
  scroll-margin-top: 80px;
}

这会偏移浏览器导航到目标元素时的着陆位置——无需 JavaScript。

无障碍:尊重 prefers-reduced-motion

有些用户会因动画滚动而出现晕动症或前庭不适。请务必遵循 prefers-reduced-motion 媒体查询:

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

此模式仅对未在操作系统设置中关闭动效的用户启用平滑滚动。这是构建无障碍滚动体验的推荐做法。

浏览器兼容性

scroll-behavior 已获得所有现代浏览器的全面支持——Chrome 61+、Firefox 36+、Edge 79+、Safari 15.4+ 和 Opera 48+。全球支持率约为 95%,当前项目无需使用 polyfill。

CSS vs. JavaScript:该选哪个?

需求最佳方案
简单的锚点链接滚动CSS
自定义时长或缓动曲线JavaScript
基于条件或逻辑的滚动JavaScript

对于大多数页内导航场景——文档站点、落地页、作品集——CSS 已经足够且更优。代码更少,无依赖,原生浏览器性能。

快速实施清单

  • ☐ 将 scroll-behavior: smooth 应用于 html,而非 body
  • ☐ 包裹在 prefers-reduced-motion: no-preference
  • ☐ 为含固定头部的布局添加 scroll-margin-top
  • ☐ 测试锚点链接和键盘导航
  • ☐ 在 iOS Safari 上验证行为表现

结语

CSS 平滑滚动是那种实现成本几乎为零、却能立即让页内导航显得更具设计感的改进之一。一条声明、放置得当,无需触及 JavaScript,就能覆盖绝大多数实际使用场景。

常见问题

scroll-behavior 属性必须设置在拥有视口的滚动容器上,也就是 html 元素。当应用于 body 时,该值不会传播到视口级滚动器,因此锚点链接滚动会回退到默认的瞬时跳转。将规则移到 html 上,平滑动画就能按预期工作。

不能。动画时长和缓动曲线完全由浏览器决定,无法通过 CSS 自定义。如果你的设计需要特定速度或自定义缓动函数,需要使用 JavaScript 方案,例如结合 behavior smooth 的 window.scrollTo 配合自定义动画逻辑,或使用一个手动处理时间控制的小型库。

不会。该属性仅作用于编程式和锚点触发的滚动,例如点击哈希链接、调用 scrollIntoView 或调用 window.scrollTo。用户通过鼠标滚轮、触控板手势、拖动滚动条或键盘方向键发起的滚动不会受到影响,仍然使用浏览器原生的滚动行为。

在目标元素上使用 scroll-margin-top 属性,值与头部高度一致。例如,对带 id 的 section 元素设置 scroll-margin-top 为 80px,会偏移滚动着陆位置,使该区域出现在固定头部下方,而不是被遮挡在头部后面。无需 JavaScript 或额外标记。

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay