スムーススクロールをCSSだけで実装【ずれる分の位置調整も】
スムーススクロールをCSSだけで実装する方法を解説します。
また、よくある「スクロールがずれる問題」の解決方法も併せてご紹介します。
CSSだけでスムーススクロールを実装する方法
scroll-behavior: smooth;
を使う
スムーススクロールをCSSで実装するには、scroll-behavior: smooth;
を<html>
に適用します。
html {
scroll-behavior: smooth;
}
scroll-behavior
はスムーススクロールをたった1行で実装できる、非常に便利なプロパティです。
ブラウザ対応も、2023年8月現在、IEや古いバージョンをカバーしない前提であれば大丈夫です。
仮にサポート外の環境から閲覧された場合も、レイアウトを組むプロパティなどと違い致命的な問題が発生するわけではないので、その意味でもリスクは少ない手法だと思います。
なお、本記事はCSSでの実装を主旨としていますが、IEや古いバージョンもカバーできるjQueryのコードも後述します。
スクロールがずれるときの位置調整
CSSでの実装に限りませんが、スムーススクロールでスクロールの位置がずれてしまうことはよく見受けられる問題です。
これには固定ヘッダーの裏に隠れるパターンと遅延読み込みによりずれるパターンの2つがありますので、以下でそれぞれの解決方法を解説します。
固定ヘッダーの裏に隠れるパターン
ヘッダーをposition: fixed
で画面上部に固定している場合、スクロール先がヘッダーの裏に隠れてしまうことが考えられます。
これを解説するには下記の2つの手法があります。
- scroll-padding-topを使う
- ネガティブマージンを使う
scroll-padding-top
を使う
scroll-padding-top
を使うことで、スクロールのターゲットになっている要素が画面の最上部にピッタリつく位置ではなく、指定した分の距離を上に空けた位置に移動するように設定できます。
html {
scroll-padding-top: 100px;
}
例えばヘッダーの高さが60px
の場合、上記のようにscroll-padding-top: 100px;
をかけていれば、ヘッダーの下40px
の位置にスクロールで移動するようになります。
scroll-behavior
と同じくIEや古いバージョンでは対応していないですが、scroll-behavior
を使っている時点でそれらの環境は考慮しない前提なので、使うのに手間がかかる可能性があるネガティブマージンよりもscroll-padding-top
を使う方がよいでしょう。
ネガティブマージンを使う
スムーススクロールのずれを解消するために昔からよく使われている手法です。
padding-top
を指定することでスクロール位置を調整し、その上でmargin-top
にpadding-top
をマイナスにした値を入れて視覚的な余白を相殺します。
h2 /* スクロール先をセレクタに */ {
margin-top: -100px;
padding-top: 100px;
}
ただ、 この場合はスクロールのターゲットになる要素をセレクタとして指定しなければなりません。scroll-padding-top
は<html>
にだけ適用すれば大丈夫なので、これと比べると手間がかかります。
また、スクロールのターゲットになる要素に目に見える余白を指定したい場合はスクロールの位置調整のためのmargin
とpadding
を直接つけられないため、<span>
などを追記する必要が生じます。
遅延読み込みによりずれるパターン
loading="lazy"
などで画像を遅延読み込みさせている場合、スクロール先がずれる可能性があります。
これを回避するには、<img>
にwidth
属性とheight
属性を指定します。そうすることで、画像が読み込まれる前から表示領域が確保され、ずれを防ぐことができます。
<img src="..." alt="..." width="画像の幅" height="画像の高さ">
(番外編)JavaScriptでスムーススクロールを実装する
CSS のscroll-behavior
はたった1行でスムーススクロールを実装できる便利なプロパティですが、古いブラウザでは使えない可能性もあります。
これらの環境をカバーしたいときは、 下記のようにJavaScriptを使う方法が考えられます。
document.addEventListener('DOMContentLoaded', () => {
// ヘッダーエレメントを取得。
const headerElement = document.querySelector('.header');
// バッファ(オフセット)の値を設定。スムーズスクロールの際にこの値だけ位置をずらす。
const buffer = 60;
// href属性の値が"#"から始まるすべての<a>要素を取得
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
// 各<a>要素にクリックイベントのリスナーを追加
anchor.addEventListener('click', (event) => {
// デフォルトのクリック動作をキャンセル
event.preventDefault();
// ヘッダーの高さを取得。ヘッダーエレメントがない場合は0とする。
const headerHeight = headerElement ? headerElement.offsetHeight : 0;
// スクロール時の総オフセットを計算(ヘッダーの高さ + バッファ)
const totalOffset = headerHeight + buffer;
// クリックされた<a>要素のhref属性の値を取得
const targetId = anchor.getAttribute('href');
// ターゲットとなるエレメントを取得
const targetElement = document.querySelector(targetId);
// IDが指定されていない、またはターゲットのエレメントが存在しない場合
if (targetId === "#" || targetId === "" || !targetElement) {
alert('適切なスクロール先がありません');
return; // 以降の処理をスキップ
}
// smoothScroll関数を呼び出してスムーズスクロールを実行
smoothScroll(targetElement, totalOffset);
});
});
// スムーズスクロールを行う関数
const smoothScroll = (target, offset) => {
// スクロール先の位置を計算
const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
// 計算された位置にスムーズにスクロール
window.scroll({
top: targetPosition,
behavior: 'smooth'
});
}
});