ページ内リンクをクリックした際に目的の場所まで滑らかにスクロールする「スムーススクロール」は、ユーザー体験を向上させる基本テクニックです。CSS だけで実現する方法から JavaScript で細かく制御する方法まで、複数のアプローチがあります。
・CSS
scroll-behavior: smooth による最もシンプルな実装・
scrollIntoView で特定要素まで滑らかにスクロールする方法・
window.scrollTo で座標を指定してスクロールする方法・固定ヘッダーがある場合のオフセット調整
・ページ内リンクすべてにスムーススクロールを適用する方法
・requestAnimationFrame によるカスタムイージング
・prefers-reduced-motion(モーション軽減)への対応
CSS scroll-behavior で実現する(最もシンプル)
JavaScript を一切書かずにスムーススクロールを実現できる最もシンプルな方法です。
html {
scroll-behavior: smooth;
}
この 1 行だけで、ページ内リンク(<a href="#section">)をクリックした際のスクロールが滑らかになります。JavaScript の scrollTo や scrollIntoView に behavior: "smooth" を指定した場合にも適用されます。
| メリット | デメリット |
|---|---|
| CSS 1 行で実装完了 | スクロール速度やイージングのカスタマイズ不可 |
| JavaScript 不要 | 固定ヘッダー分のオフセット調整ができない |
| すべてのページ内リンクに自動適用 | Safari は 15.4 以降で対応(古い iOS は非対応) |
scroll-behavior だけで十分です。scrollIntoView で特定要素までスクロールする
scrollIntoView は、指定した要素が画面内に表示されるように自動でスクロールするメソッドです。behavior: "smooth" を指定すると滑らかにスクロールします。
const target = document.getElementById("section2");
target.scrollIntoView({
behavior: "smooth", // 滑らかにスクロール
block: "start" // 要素の上端をビューポートの上端に合わせる
});
block オプションの種類
block は要素の垂直方向の表示位置を指定します。
| 値 | 動作 |
|---|---|
"start"(デフォルト) |
要素の上端をビューポートの上端に合わせる |
"center" |
要素をビューポートの中央に表示 |
"end" |
要素の下端をビューポートの下端に合わせる |
"nearest" |
最も近い位置にスクロール(すでに表示されていればスクロールしない) |
document.getElementById("goToFaq").addEventListener("click", () => {
document.getElementById("faq-section").scrollIntoView({
behavior: "smooth",
block: "start"
});
});
window.scrollTo で座標を指定してスクロールする
window.scrollTo はスクロール先の座標(ピクセル)を直接指定するメソッドです。behavior: "smooth" を付けると滑らかにスクロールします。
// ページ上部にスムーススクロール
window.scrollTo({
top: 0,
behavior: "smooth"
});
// 500px の位置にスムーススクロール
window.scrollTo({
top: 500,
behavior: "smooth"
});
特定要素の位置を計算してスクロールする
scrollIntoView と違い、scrollTo は座標を自分で計算するため、固定ヘッダーの高さを引いたオフセット調整が簡単に行えます。
function scrollToElement(selector, offset = 0) {
const element = document.querySelector(selector);
if (!element) return;
const top = element.getBoundingClientRect().top + window.scrollY - offset;
window.scrollTo({
top: top,
behavior: "smooth"
});
}
// 固定ヘッダーの高さ(60px)を考慮してスクロール
scrollToElement("#target-section", 60);
getBoundingClientRect().top はビューポートからの相対位置を返します。window.scrollY(現在のスクロール量)を足すことで、ページ全体における絶対位置を計算できます。固定ヘッダーがある場合のオフセット調整
固定ヘッダー(position: fixed / sticky)があるサイトでは、スクロール先の要素がヘッダーの下に隠れてしまう問題が起きます。対処法は 2 つあります。
CSS scroll-padding-top を使う方法
CSS だけでオフセットを調整できる最もシンプルな方法です。scroll-behavior: smooth と組み合わせると、JavaScript なしで固定ヘッダー対応のスムーススクロールが完成します。
html {
scroll-behavior: smooth;
scroll-padding-top: 70px; /* 固定ヘッダーの高さ + 余白 */
}
scroll-padding-top は scrollIntoView にも適用されるため、JavaScript でスクロールする場合にも有効です。
JavaScript で動的にオフセットを計算する方法
ヘッダーの高さがレスポンシブで変わる場合は、JavaScript で動的に取得します。
function scrollToWithHeader(selector) {
const element = document.querySelector(selector);
if (!element) return;
// ヘッダーの高さを動的に取得
const header = document.querySelector("header");
const headerHeight = header ? header.offsetHeight : 0;
const margin = 16; // 追加の余白
const top = element.getBoundingClientRect().top + window.scrollY - headerHeight - margin;
window.scrollTo({ top, behavior: "smooth" });
}
すべてのページ内リンクにスムーススクロールを適用する
サイト内のページ内リンク(href="#xxx")すべてにスムーススクロールを適用するコードです。固定ヘッダーのオフセット調整も含めた実用的な実装です。
document.addEventListener("DOMContentLoaded", () => {
// href="#" で始まるすべてのアンカーリンクを取得
const anchors = document.querySelectorAll('a[href^="#"]');
anchors.forEach(anchor => {
anchor.addEventListener("click", (e) => {
const href = anchor.getAttribute("href");
if (href === "#" || href === "#top") {
// ページ上部にスクロール
e.preventDefault();
window.scrollTo({ top: 0, behavior: "smooth" });
return;
}
const target = document.querySelector(href);
if (!target) return;
e.preventDefault();
// 固定ヘッダーの高さを考慮
const header = document.querySelector("header");
const offset = header ? header.offsetHeight + 16 : 0;
const top = target.getBoundingClientRect().top + window.scrollY - offset;
window.scrollTo({ top, behavior: "smooth" });
});
});
});
scroll-behavior: smooth + scroll-padding-top で済む場合は、JavaScript は不要です。上記の JavaScript パターンは、スクロール完了後のコールバック処理やアニメーションの細かい制御が必要な場合に使いましょう。requestAnimationFrame でカスタムイージングを実装する
ネイティブの behavior: "smooth" はスクロール速度やイージング(加減速のカーブ)をカスタマイズできません。独自のアニメーションが必要な場合は requestAnimationFrame で自前実装します。
function smoothScrollTo(targetY, duration = 600) {
const startY = window.scrollY;
const distance = targetY - startY;
let startTime = null;
// イーズインアウト(加速→減速)
function easeInOutCubic(t) {
return t < 0.5
? 4 * t * t * t
: 1 - Math.pow(-2 * t + 2, 3) / 2;
}
function step(currentTime) {
if (!startTime) startTime = currentTime;
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
window.scrollTo(0, startY + distance * easeInOutCubic(progress));
if (progress < 1) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
}
// 使用例: #target まで 800ms でスクロール
const target = document.getElementById("target");
const targetY = target.getBoundingClientRect().top + window.scrollY;
smoothScrollTo(targetY, 800);
よく使われるイージング関数
| イージング | 動き | 数式 |
|---|---|---|
| easeInQuad | ゆっくり始まる | t * t |
| easeOutQuad | ゆっくり止まる | t * (2 - t) |
| easeInOutCubic | 滑らかに加速→減速 | 上記コード参照 |
| linear | 等速(一定速度) | t |
easeInOutCubic が最も自然で多くのサイトで使われています。prefers-reduced-motion への対応(アクセシビリティ)
一部のユーザーは OS 設定で「視差効果を減らす」を有効にしています。この設定を尊重し、スムーススクロールを無効化(即座にジャンプ)するのがアクセシビリティのベストプラクティスです。
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
function getScrollBehavior() {
const prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
return prefersReduced ? "auto" : "smooth";
}
// 使用例
window.scrollTo({
top: 0,
behavior: getScrollBehavior()
});
prefers-reduced-motion への対応を検討してください。スムーススクロール実装方法の比較
| 方法 | 固定ヘッダー対応 | 速度カスタム | JS 不要 | IE 対応 |
|---|---|---|---|---|
CSS scroll-behavior |
scroll-padding-top で対応 |
× | ○ | × |
scrollIntoView |
scroll-padding-top で対応 |
× | × | △(smooth 非対応) |
window.scrollTo |
○(座標計算で対応) | × | × | △(smooth 非対応) |
requestAnimationFrame |
○ | ○ | × | ○ |
関連記事
- スムーススクロールで「ページ上部へ戻る」ボタンを実装する方法
- 特定の位置までスクロールさせる方法 — scrollTo・scrollIntoView の詳細
- 上向きスクロールで表示されるヘッダーの作り方
- CSS scroll-behavior でスムーズスクロールを実装する方法
- アンカーリンクのスムーススクロールを実装する方法
- アコーディオンの作り方 — CSS アニメーション・アクセシビリティ対応
よくある質問
scroll-padding-top を併用すれば、固定ヘッダーへの対応も CSS だけで可能です。html { scroll-padding-top: 70px; }(ヘッダー高さ分)を設定するのが最もシンプルです。JavaScript で対応する場合は getBoundingClientRect().top + window.scrollY - headerHeight で座標を計算し、window.scrollTo に渡します。behavior: "smooth" では速度を指定できません。速度やイージングをカスタマイズしたい場合は requestAnimationFrame を使って自前で実装します。duration(ミリ秒)とイージング関数を変えることで自由に調整できます。@media (prefers-reduced-motion: reduce) で scroll-behavior: auto に切り替えるだけなので、対応コストはごくわずかです。まとめ
スムーススクロールの実装は、要件に応じて適切な手法を選ぶことが重要です。
- シンプルなページ内リンク → CSS
scroll-behavior: smooth(1 行で完了) - ボタンクリックで特定要素へ →
scrollIntoView({ behavior: "smooth" }) - 固定ヘッダー対応 → CSS
scroll-padding-topまたはwindow.scrollToでオフセット計算 - 速度やイージングのカスタム →
requestAnimationFrameで自前実装
どの方法でも、prefers-reduced-motion への対応を忘れないようにしましょう。アクセシビリティを考慮したスムーススクロールは、すべてのユーザーにとって快適なブラウジング体験を提供します。

