scroll-behavior
ページ内リンクなどで起きるスクロールを、滑らかにするプロパティ。 JavaScriptで少しずつ動かしていたスクロールを、CSSの指定に置き換えられます。
今の書き方
htmlに指定すると、ページ内リンクでもscrollTo()でも、スクロールが滑らかになります。
移動はブラウザに任せるため、URLの#や:targetも、リンク本来のとおりに更新されます。
@media (prefers-reduced-motion: no-preference) {
html {
scroll-behavior: smooth;
}
}以前の書き方
ページ内リンクのクリックを止め、JavaScriptでスクロール位置を少しずつ動かしていました。
クリックを止めるため、URLの#は変わらず、:targetも当たりません。
for (const link of document.querySelectorAll('a[href^="#"]')) {
link.addEventListener('click', (event) => {
const id = decodeURIComponent(link.hash.slice(1))
const target = document.getElementById(id)
if (!target) return
event.preventDefault()
const start = window.scrollY
const distance = target.getBoundingClientRect().top
const duration = 500
let startTime
const step = (time) => {
if (startTime === undefined) startTime = time
const progress = Math.min((time - startTime) / duration, 1)
window.scrollTo(0, start + distance * progress)
if (progress < 1) requestAnimationFrame(step)
}
requestAnimationFrame(step)
})
}置き換えるときの注意点
htmlに指定します。bodyに指定しても、ページ全体のスクロールには効きません。- 滑らかになるのは、リンクや
scrollTo()などで起きるスクロールだけです。 ホイールやキーボードでのスクロールは変わりません。 - すでにある
scrollTo()やscrollIntoView()も滑らかになり、呼んだ直後はまだ移動先に着いていません。 呼んだ直後にscrollYを読んで使うコードがあれば、動きが変わります。 - 以前の書き方のコードは、消してから置き換えます。
残したままにすると、1フレームごとの
scrollTo()まで滑らかになり、移動が遅れたあとで跳びます。 - 移動にかかる時間や速さは、ブラウザが決めるため指定できません。 以前の書き方で時間を決めていた場合は、同じ動きにはなりません。 ブラウザがこの指定を無視することも、仕様で認められています。
prefers-reduced-motionで囲むのは、OSで動きを減らす設定にしている人には、瞬時に移動させるためです。- 固定ヘッダーの下に移動先が隠れる場合は、移動先に
scroll-margin-topを指定します。 固定ヘッダーの高さをJavaScriptで引いていた場合は、その計算もCSSに移せます。
フォールバック
未対応のブラウザでは瞬時に移動するだけで、リンクの移動そのものはできます。 対象の5ブラウザはすべて対応済みなので、通常は以前の書き方を残す必要はありません。 古いブラウザでも滑らかさが欠かせない場合だけ、未対応のときに以前の書き方のコードを動かします。
if (
!('scrollBehavior' in document.documentElement.style) &&
matchMedia('(prefers-reduced-motion: no-preference)').matches
) {
// 以前の書き方のコード
}Browser Support
Chrome
v61+2017-09-05Edge
v79+2020-01-15Safari
v15.4+2022-03-14Safari (iOS)
v15.4+2022-03-14Firefox
v36+2015-02-24