🕌

指定位置までスクロールする | HTML + CSS + JavaScript

に公開

概要

HTML、CSS、JavaScript を触っていると、アンカーリンクなどで、指定位置までスクロールしたいことが多々ある。今回は、HTML、CSS、JavaScript で制御可能なスクロール方法をまとめた。

HTML および CSS だけでスクロール

ハッシュを使ったページ内リンク

Web の黎明期から存在する、ページ内ナビゲーションの伝統的なメカニズム。これは、<a> 要素の href 属性にハッシュ(#)とターゲット要素の id を指定することで機能する。

<a href="#section-2">セクション2へ移動</a>
<h2 id="section-2">セクション2</h2>

このリンクをクリックすると、ブラウザは id="section-2" を持つ要素がビューポートの最上部に表示されるよう、瞬時にスクロール位置を「ジャンプ」させる。この動作は、アニメーションを伴わない即時のものである。

CSS によるスムーズスクロール

この伝統的なアンカーリンクの挙動を、CSSのみで変更する機能がscroll-behaviorプロパティである。スクロールコンテナにscroll-behavior: smooth;を指定するだけで、前述のアンカーリンクによる「即時ジャンプ」が、ブラウザネイティブのスムーズなアニメーション(スムーズスクロール)に置き換わる。また、scroll-padding-topが利用可能である。このプロパティをスクロールコンテナに設定すると、アンカーリンクや後述する JavaScript API によるスクロール停止位置が、ビューポートの最上部から指定したピクセル分だけ下がる 。

html {
    scroll-behavior: smooth;
    scroll-padding-top: 100px;
}

Element.scrollIntoView() による自動スクロール

JavaScript を使用して、特定の要素をビューポート内に表示させるための標準的なメソッドがElement.scrollIntoView()である。これは、CSS アプローチよりも詳細な制御を可能にする。
以下は引数にブール値を設定する例である。この方法ではスムーズスクロールはできず、アンカーリンクと同様の「即時ジャンプ」となる。

const element = document.getElementById('target-element');
// 要素の上端をビューポートの上端に合わせる
element.scrollIntoView(true);
// 要素の下端をビューポートの下端に合わせる
element.scrollIntoView(false);

現代のブラウザでは、scrollIntoView()の引数にscrollIntoViewOptionsというオブジェクトを渡すことで、スクロールの挙動を詳細に制御できる。

const element = document.getElementById('target-element');
// オプションオブジェクトを使用した現代的な使用法
element.scrollIntoView({
  behavior: 'smooth',
  block: 'center',
  inline: 'nearest',
});

このオブジェクトは、behaviorblockinlineという 3 つの主要なプロパティを持つ。

behavior プロパティ(挙動)

スクロールのトランジション(アニメーション)を定義する。

  • auto (デフォルト): スクロール挙動は、CSS のscroll-behaviorプロパティの計算値によって決まる。
  • instant: スムーズスクロールを無効化し、即時ジャンプする。
  • smooth: スムーズなアニメーションでスクロールする。

block プロパティ(垂直位置)

垂直方向(ブロック方向)の配置を定義する。

  • start (デフォルト): 要素の上端を、スクロール可能なコンテナの上端に合わせる。
  • center: 要素を、スクロール可能なコンテナの垂直中央に配置する。
  • end: 要素の下端を、スクロール可能なコンテナの下端に合わせる。
  • nearest: 要素がビューポートの最も近い端に配置されるようスクロールする。要素がすでにビューポート内にほぼ収まっている場合、スクロール距離は最小限に抑えられる。

inline プロパティ(水平位置)

水平方向(インライン方向)の配置を定義する。

  • start: 要素の左端を、スクロール可能なコンテナの左端に合わせる。
  • center: 要素を、スクロール可能なコンテナの水平中央に配置する。
  • end: 要素の右端を、スクロール可能なコンテナの右端に合わせる。
  • nearest (デフォルト): 水平方向のスクロール距離が最小になるよう配置する。

window.scrollTo() によるスクロール

どうしてもオフセット付きのスムーズスクロールを実現したい場合に使用する、より低レベルな API。ドキュメント全体のスクロール位置を指定した絶対座標に移動させるためのメソッドである。ピクセル単位の完璧なオフセット制御とスムーズスクロールの両立を実現する。

基本的な使用法(座標の指定)

最も単純な使い方は、X(横)とY(縦)の座標を指定して瞬時にジャンプする方法。

// ページの最上部(X=0, Y=0)に瞬時にジャンプする
window.scrollTo(0, 0);

オプションオブジェクトによるスムーズスクロール

モダンブラウザでは、optionsオブジェクトを渡すことで、スムーズスクロールを実現できる。

// ドキュメントの上から 1000px の位置までスムーズにスクロールする
window.scrollTo({
  top: 1000,
  left: 0,
  behavior: 'smooth'
});

実践的なコード: 固定ヘッダー(オフセット)対応のスムーズスクロール

window.scrollTo() が最も強力に機能するのは、scrollIntoView() ではネイティブにサポートされていない「オフセット(余白)」を考慮したスクロールの実装。
例えば、position: fixed の固定ヘッダーがある場合、scrollIntoView() で要素をビューポートの start(上端)に合わせると、要素がヘッダーの下に隠れてしまう。
window.scrollTo() を使うと、このヘッダーの高さを手動で計算し、ヘッダーのすぐ下にターゲット要素が停止するようにスクロール位置を厳密に制御できる。
以下に、ID(例:'targetElement')を持つ要素へ、指定したオフセット(例:45px)を空けてスムーズにスクロールする関数を示す。

/**
 * 指定したIDの要素へ、固定ヘッダーの高さを考慮してスムーズスクロールする
 * @param {string} targetId - スクロール先の要素のID
 * @param {number} headerOffset - 固定ヘッダーの高さ(ピクセル)
 */
function scrollToTargetAdjusted(targetId, headerOffset = 45) {
    
    // 1. ターゲット要素を取得
    const element = document.getElementById(targetId);
    if (!element) return; // 要素が存在しない場合は終了

    // 2. ターゲット要素の「ビューポート上端からの相対位置」を取得
    const elementPosition = element.getBoundingClientRect().top;

    // 3. 現在の「ページのスクロール量」を取得
    // window.pageYOffset は古い表現で、現在は window.scrollY が推奨される
    const currentScroll = window.scrollY || window.pageYOffset;

    // 4. 最終的なスクロール位置(絶対座標)を計算
    // (要素の相対位置 + 現在のスクロール量) - ヘッダーの高さ
    const offsetPosition = elementPosition + currentScroll - headerOffset;

    // 5. 計算した位置へスムーズにスクロールを実行
    window.scrollTo({
        top: offsetPosition,
        behavior: "smooth"
    });
}

// --- 実行例 ---
// 'section-2' というIDの要素へ、ヘッダーの高さ 45px を考慮してスクロールする場合
// scrollToTargetAdjusted('section-2', 45);

Discussion