指定位置までスクロールする | 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',
});
このオブジェクトは、behavior、block、inlineという 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