ページ内のセクションへ移動するアンカーリンク(# リンク)を、カクッと飛ぶのではなくスーッとスクロールさせる方法を解説します。実は、まずはCSSだけで実装できます。
scroll-behavior: smooth(JS不要)。固定ヘッダーで位置がずれるならscroll-margin-topで調整します。クリックを細かく制御したいときだけ、JavaScriptの scrollIntoView /scrollTo を使います。完成イメージ(動くデモ)
↓ 上のボタンを押すと、枠内の対応するセクションへスーッとスクロールします:
このデモは枠(コンテナ)に scroll-behavior: smooth を付け、ボタンで scrollTo しています。ページ全体のアンカーリンクなら、次のCSSだけで同じ効果が得られます。
最も簡単:CSSの scroll-behavior(JS不要)
html に scroll-behavior: smooth を指定するだけで、ページ内のすべてのアンカーリンクがスムーススクロールになります。JavaScriptは不要です。
html {
scroll-behavior: smooth;
}
固定ヘッダーがあって移動先がヘッダーに隠れる場合は、移動先の要素に scroll-margin-top を付けるだけで余白を確保できます。
/* 移動先のセクションに付ける */
.section {
scroll-margin-top: 60px; /* ヘッダーの高さ分 */
}
CSSの scroll-behavior の詳細はscroll-behaviorでスムーズスクロールを実装する方法で解説しています。多くのサイトはこれだけで十分です。
JavaScriptで実装する:scrollIntoView
クリック時に追加の処理を入れたい(メニューを閉じる、URLを更新するなど)場合は、JavaScriptで制御します。a[href^="#"] のクリックを拾い、scrollIntoView({ behavior: "smooth" }) でスクロールします。
document.querySelectorAll('a[href^="#"]').forEach((link) => {
link.addEventListener("click", (e) => {
const id = link.getAttribute("href").slice(1);
const target = document.getElementById(id);
if (!target) return;
e.preventDefault(); // 既定のジャンプを止める
target.scrollIntoView({ behavior: "smooth", block: "start" });
});
});
JavaScriptで preventDefault() すると、URLのハッシュ(#section)が更新されません。戻るボタンや共有に対応させたいなら、スクロール後に history.pushState でハッシュを書き換えます。
target.scrollIntoView({ behavior: "smooth", block: "start" });
// 戻る/共有に対応:URLのハッシュを更新(履歴に積む)
history.pushState(null, "", "#" + id);
固定ヘッダーのオフセット調整:scrollTo
JavaScript側で位置を細かく調整したいなら window.scrollTo を使います。要素の位置に window.scrollY を足し、ヘッダーの高さ分を引きます。
const HEADER = 60; // 固定ヘッダーの高さ
const top = target.getBoundingClientRect().top + window.scrollY - HEADER;
window.scrollTo({ top, behavior: "smooth" });
scrollTo と scrollIntoView の違いや、任意の位置へスクロールする方法は特定の位置までスクロールする方法(scrollTo・scrollIntoView)で詳しく解説しています。
prefers-reduced-motionに配慮する
動きが苦手なユーザーのために、OSで「視差効果を減らす」が有効なときはスムーススクロールを無効化すると親切です。CSSなら次のように書けます。
@media (prefers-reduced-motion: no-preference) {
html { scroll-behavior: smooth; }
}
scrollIntoView を使うときも、window.matchMedia("(prefers-reduced-motion: reduce)").matches が true ならbehavior: "auto"(即移動)に切り替えると配慮できます。よくある質問(FAQ)
html { scroll-behavior: smooth; } です(JS不要)。JavaScriptなら、a タグの click で preventDefault() し、対象要素に scrollIntoView({ behavior: "smooth" }) を呼びます。scroll-margin-top: ヘッダー高さ を付けるのが簡単です。JavaScriptなら getBoundingClientRect().top + window.scrollY - ヘッダー高さ を計算してwindow.scrollTo を使います。DOMContentLoaded で location.hash を確認し、ハッシュがあれば対象要素に scrollIntoView({ behavior: "smooth" }) を呼びます。URLに #section が付いていても即ジャンプにならないよう制御できます。scroll-behavior で十分です。クリック時にメニューを閉じる・URLを書き換えるなど追加処理が必要なときだけJavaScriptを使います。まとめ
アンカーリンクのスムーススクロールは、まずCSSの scroll-behavior: smooth で実装するのが最も簡単です。固定ヘッダーのずれは scroll-margin-top で調整します。
クリック時に追加処理を入れたいときだけ、JavaScriptの scrollIntoView やscrollTo(オフセット調整)を使いましょう。prefers-reduced-motion への配慮も忘れずに。

