JavaScriptで特定の位置までスクロールさせたい場面は頻繁にあります。「トップに戻る」ボタン、ページ内リンク、フォームのエラー箇所へのジャンプなど、スクロール制御はWebアプリに欠かせない機能です。
この記事では、window.scrollTo()・Element.scrollIntoView()・window.scrollBy() の3つのメソッドを中心に、スムーズスクロールの実装方法から実務パターンまで体系的に解説します。
この記事で学べること
- window.scrollTo() で絶対位置にスクロールする方法
- Element.scrollIntoView() で要素を画面内に表示する方法
- window.scrollBy() で相対的にスクロールする方法
- CSS・JavaScriptによるスムーズスクロールの実装
- トップに戻るボタン・ページ内リンク・エラー箇所ジャンプなど実務パターン
- 固定ヘッダーとの重なり対策・ブラウザ互換性の注意点
window.scrollTo() の使い方
window.scrollTo() はページ全体を指定した座標(絶対位置)にスクロールさせるメソッドです。最もシンプルなスクロール制御方法で、ページの特定のY座標にジャンプしたいときに使います。
基本構文(x, y座標指定)
引数に x座標(水平方向)と y座標(垂直方向)をピクセル単位で指定します。
JavaScript
// 基本構文: window.scrollTo(x座標, y座標)
// ページ最上部へスクロール
window.scrollTo(0, 0);
// 垂直方向に500pxの位置へスクロール
window.scrollTo(0, 500);
// 水平・垂直の両方を指定
window.scrollTo(100, 300);
scrollTo() の座標はページ左上を原点(0, 0)とした絶対位置です。現在のスクロール位置に関係なく、常に同じ位置に移動します。
オプション指定(behavior: 'smooth')
オブジェクト形式で引数を渡すと、スクロールの挙動(behavior)を指定できます。behavior: 'smooth' でスムーズスクロールになります。
JavaScript
// オプション形式(推奨)
window.scrollTo({
top: 500, // 垂直方向の位置(px)
left: 0, // 水平方向の位置(px)
behavior: 'smooth' // スムーズスクロール
});
// behavior の値
// 'smooth' : なめらかにスクロール
// 'instant': 即座にジャンプ
// 'auto' : ブラウザのデフォルト動作
ページ最上部・最下部へのスクロール
よく使うパターンとして、ページの最上部と最下部へのスクロール方法を紹介します。
JavaScript
// ページ最上部へスムーズスクロール
window.scrollTo({
top: 0,
behavior: 'smooth'
});
// ページ最下部へスムーズスクロール
window.scrollTo({
top: document.documentElement.scrollHeight,
behavior: 'smooth'
});
// 特定要素の位置までスクロール
const element = document.getElementById('target');
const rect = element.getBoundingClientRect();
window.scrollTo({
top: rect.top + window.scrollY,
behavior: 'smooth'
});
ポイント:document.documentElement.scrollHeight はページ全体の高さを返します。これを top に指定すると、ページの最下部までスクロールできます。
Element.scrollIntoView() の使い方
Element.scrollIntoView() は、指定した要素が画面内に表示されるようにスクロールするメソッドです。scrollTo() と違い、座標を計算する必要がなく、要素を直接指定できるため実務では最もよく使われます。
基本構文
引数なし、またはboolean値を渡すシンプルな使い方から見ていきましょう。
JavaScript
const target = document.getElementById('section-2');
// 引数なし(デフォルト): 要素の上端がビューポートの上端に来る
target.scrollIntoView();
// true: 要素の上端がビューポートの上端に来る(デフォルトと同じ)
target.scrollIntoView(true);
// false: 要素の下端がビューポートの下端に来る
target.scrollIntoView(false);
オプション(behavior, block, inline)
オブジェクト形式で細かい制御が可能です。behavior(スクロール挙動)、block(垂直方向の配置)、inline(水平方向の配置)の3つのプロパティを指定できます。
JavaScript
target.scrollIntoView({
behavior: 'smooth', // 'smooth' | 'instant' | 'auto'
block: 'start', // 'start' | 'center' | 'end' | 'nearest'
inline: 'nearest' // 'start' | 'center' | 'end' | 'nearest'
});
| プロパティ |
値 |
説明 |
| behavior |
smooth |
なめらかにアニメーション |
| behavior |
instant |
即座にジャンプ |
| block |
start |
要素の上端をビューポートの上端に合わせる |
| block |
center |
要素を画面の中央に表示 |
| block |
end |
要素の下端をビューポートの下端に合わせる |
| block |
nearest |
最も近い端に合わせる(スクロール量最小) |
| inline |
nearest |
水平方向も最も近い端に合わせる(デフォルト) |
固定ヘッダーがある場合のずれ対策(scroll-margin-top)
scrollIntoView() を使うと、固定ヘッダー(position: fixed)の下に要素が隠れてしまうことがあります。これは CSSの scroll-margin-top で簡単に解決できます。
CSS
/* 固定ヘッダーの高さ分だけ余白を確保 */
scroll-margin-top: 80px;
/* スクロールターゲットになりうる全見出しに適用 */
h2, h3, h4 {
scroll-margin-top: 80px;
}
JavaScript
// CSSで scroll-margin-top を指定済みなら
// scrollIntoView() はその余白を考慮してスクロールする
const heading = document.getElementById('section-title');
heading.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
// JavaScript側で手動計算する方法もある
const headerHeight = 80;
const targetPos = heading.getBoundingClientRect().top + window.scrollY - headerHeight;
window.scrollTo({
top: targetPos,
behavior: 'smooth'
});
ポイント:scroll-margin-top はCSSだけで完結するため、JavaScriptで座標計算するよりもシンプルです。固定ヘッダーがあるサイトでは、スクロールターゲットに設定しておくことをおすすめします。
window.scrollBy() で相対的にスクロール
window.scrollBy() は、現在のスクロール位置から相対的に移動するメソッドです。scrollTo() が「ページ上のどこへ移動するか(絶対位置)」なのに対し、scrollBy() は「今の位置からどれだけ動かすか(相対位置)」を指定します。
JavaScript
// 基本構文: window.scrollBy(x, y)
// 下に300pxスクロール
window.scrollBy(0, 300);
// 上に200pxスクロール(負の値)
window.scrollBy(0, -200);
// オプション形式(スムーズスクロール)
window.scrollBy({
top: 500,
left: 0,
behavior: 'smooth'
});
// 1画面分スクロール
window.scrollBy({
top: window.innerHeight,
behavior: 'smooth'
});
| メソッド |
スクロール方式 |
使いどころ |
| scrollTo() |
絶対位置 |
特定の座標に移動したいとき |
| scrollBy() |
相対位置 |
今の位置から一定量動かしたいとき |
| scrollIntoView() |
要素指定 |
特定の要素を画面に表示したいとき |
スムーズスクロールの実装パターン
スムーズスクロールにはいくつかの実装方法があります。目的に応じて最適な方法を選びましょう。
CSS scroll-behavior: smooth(最もシンプル)
CSSだけでページ全体のスクロール動作をスムーズにできます。JavaScriptを1行も書かずにスムーズスクロールを実現する最もシンプルな方法です。
CSS
/* ページ全体のスクロールをスムーズに */
html {
scroll-behavior: smooth;
}
/* アクセシビリティ対応: アニメーション無効設定のユーザーには即座にスクロール */
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
HTML
<!-- CSSだけでアンカーリンクがスムーズスクロールになる -->
<a href="#section1">セクション1へ</a>
<a href="#section2">セクション2へ</a>
<h2 id="section1">セクション1</h2>
<h2 id="section2">セクション2</h2>
scroll-behavior: smooth を html 要素に設定するだけで、ページ内のアンカーリンク(#id)クリック時と、JavaScriptの scrollTo()/scrollIntoView() でのスクロールがすべてスムーズになります。
JavaScriptの behavior: 'smooth'
CSSではなくJavaScriptでスムーズスクロールを制御したい場合は、各スクロールメソッドのオプションに behavior: 'smooth' を指定します。特定の操作だけをスムーズにしたい場合に便利です。
JavaScript
// scrollTo() でスムーズスクロール
window.scrollTo({ top: 0, behavior: 'smooth' });
// scrollIntoView() でスムーズスクロール
element.scrollIntoView({ behavior: 'smooth' });
// scrollBy() でスムーズスクロール
window.scrollBy({ top: 300, behavior: 'smooth' });
アンカーリンクのスムーズスクロール(JavaScript実装)
CSSの scroll-behavior が使えない環境や、より細かい制御が必要な場合は、JavaScriptでアンカーリンクのスムーズスクロールを実装します。
JavaScript
// ページ内アンカーリンクをすべてスムーズスクロールに
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
const targetElement = document.querySelector(targetId);
if (targetElement) {
targetElement.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
注意:e.preventDefault() を忘れると、アンカーリンク本来の動作(一瞬でジャンプ)が先に実行されてしまいます。必ず呼び出しましょう。
実務でよく使うパターン集
ここからは、実際のWebサイト・Webアプリで頻繁に登場するスクロール制御のパターンを紹介します。コピー&ペーストですぐに使えるコードを揃えました。
「トップに戻る」ボタン
一定量スクロールすると表示される「トップに戻る」ボタンは、多くのサイトで実装されています。表示/非表示の切り替えとスムーズスクロールを組み合わせます。
HTML
<button id="back-to-top" aria-label="ページトップに戻る">
▲ TOP
</button>
CSS
#back-to-top {
position: fixed;
bottom: 20px;
right: 20px;
padding: 12px 16px;
background: #0284c7;
color: #fff;
border: none;
border-radius: 8px;
cursor: pointer;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s, visibility 0.3s;
z-index: 1000;
}
#back-to-top.visible {
opacity: 1;
visibility: visible;
}
JavaScript
const backToTopBtn = document.getElementById('back-to-top');
// スクロール量に応じてボタンの表示/非表示を切り替え
window.addEventListener('scroll', () => {
if (window.scrollY > 300) {
backToTopBtn.classList.add('visible');
} else {
backToTopBtn.classList.remove('visible');
}
});
// クリックでトップへスムーズスクロール
backToTopBtn.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
ページ内リンク(目次からのジャンプ)
ブログや技術文書でよくある、目次から各セクションへジャンプするパターンです。固定ヘッダーのオフセットも考慮した実装を紹介します。
JavaScript
// 固定ヘッダーを考慮したページ内リンク
function scrollToSection(sectionId, offset = 80) {
const target = document.getElementById(sectionId);
if (!target) return;
const elementTop = target.getBoundingClientRect().top;
const scrollPosition = elementTop + window.scrollY - offset;
window.scrollTo({
top: scrollPosition,
behavior: 'smooth'
});
}
// 目次リンクにイベント設定
document.querySelectorAll('.toc-link').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const id = link.getAttribute('href').slice(1);
scrollToSection(id);
});
});
フォームバリデーション後にエラー箇所へスクロール
長いフォームでバリデーションエラーが発生した際、最初のエラー箇所を画面に表示するのはUXの基本です。
JavaScript
function validateAndScroll() {
const errors = validateForm();
if (errors.length > 0) {
// エラーメッセージを表示
showErrors(errors);
// 最初のエラー要素を取得
const firstError = document.querySelector('.error');
if (firstError) {
// エラー箇所を画面中央に表示
firstError.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
// フォーカスも移動(アクセシビリティ)
firstError.focus({ preventScroll: true });
}
return false;
}
return true;
}
ポイント:focus({ preventScroll: true }) を使うと、フォーカス移動時の自動スクロールを防ぎ、scrollIntoView() のスムーズスクロールを活かせます。
無限スクロールでの位置保持
無限スクロール(Infinite Scroll)では、コンテンツを追加した後にスクロール位置がずれないよう、位置の保存と復元が重要になります。
JavaScript
class InfiniteScroll {
constructor(container, loadMore) {
this.container = container;
this.loadMore = loadMore;
this.isLoading = false;
this.init();
}
init() {
// スクロール監視にIntersection Observerを使用
const sentinel = document.createElement('div');
sentinel.id = 'scroll-sentinel';
this.container.appendChild(sentinel);
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && !this.isLoading) {
this.load();
}
});
observer.observe(sentinel);
}
async load() {
this.isLoading = true;
// 現在のスクロール位置を保存
const scrollPos = window.scrollY;
await this.loadMore();
// 位置を復元(上方向にコンテンツ追加した場合)
window.scrollTo(0, scrollPos);
this.isLoading = false;
}
}
よくあるミスと注意点
スクロール制御は一見シンプルですが、実装時にハマりやすいポイントがいくつかあります。
scrollIntoView() のブラウザ対応
scrollIntoView() 自体はほぼすべてのブラウザで使えますが、オプション(オブジェクト引数)のサポート状況には注意が必要です。
| 機能 |
Chrome |
Firefox |
Safari |
Edge |
| scrollIntoView(boolean) |
1+ |
1+ |
3+ |
12+ |
| scrollIntoView(options) |
61+ |
36+ |
15.4+ |
79+ |
| scrollTo(options) |
45+ |
36+ |
15.4+ |
79+ |
| scroll-behavior CSS |
61+ |
36+ |
15.4+ |
79+ |
2024年以降、主要ブラウザはすべてオプション引数をサポートしています。古いSafari(15.3以前)を考慮する場合は、polyfillの smoothscroll-polyfill を導入するか、boolean引数にフォールバックしましょう。
固定ヘッダーとの重なり問題
固定ヘッダー(position: fixed や position: sticky)があるサイトでは、スクロール先の要素がヘッダーの下に隠れてしまう問題がよく発生します。
JavaScript
// 方法1: CSS scroll-margin-top(推奨)
// CSSで対象要素に設定するだけ
// 方法2: JavaScript で offsetを計算
function scrollToWithOffset(element, offset = 0) {
const y = element.getBoundingClientRect().top
+ window.scrollY
- offset;
window.scrollTo({ top: y, behavior: 'smooth' });
}
// 方法3: ヘッダー高さを動的に取得
function scrollToElement(element) {
const header = document.querySelector('header');
const headerHeight = header ? header.offsetHeight : 0;
const y = element.getBoundingClientRect().top
+ window.scrollY
- headerHeight
- 16; // 余白
window.scrollTo({ top: y, behavior: 'smooth' });
}
SPA(Single Page Application)でのスクロール位置リセット
React や Vue.js などのSPAでは、ページ遷移時にスクロール位置がリセットされないことがあります。ルーティング時にスクロール位置を管理する必要があります。
JavaScript(React Router v6)
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
// ページ遷移時にトップへスクロールするコンポーネント
function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
JavaScript(Vue Router)
// Vue Router のスクロール動作設定
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
// ブラウザバック時は位置を復元
if (savedPosition) {
return savedPosition;
}
// ハッシュがある場合はその要素へ
if (to.hash) {
return {
el: to.hash,
behavior: 'smooth'
};
}
// それ以外はトップへ
return { top: 0 };
}
});
注意:SPAではページ遷移がブラウザのネイティブなページ読み込みではないため、スクロール位置が自動リセットされません。フレームワークのルーター機能でスクロール制御を明示的に設定しましょう。
scroll イベントのパフォーマンス
scrollイベントは高頻度で発火するため、ハンドラ内で重い処理を行うとパフォーマンスに影響します。throttleやrequestAnimationFrameで制御しましょう。
JavaScript
// 悪い例: scrollイベントで毎回重い処理
window.addEventListener('scroll', () => {
// 毎回実行される(1秒間に数十回)
heavyOperation();
});
// 良い例: requestAnimationFrameで間引く
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
window.requestAnimationFrame(() => {
handleScroll();
ticking = false;
});
ticking = true;
}
});
// 良い例: passive オプションでスクロール性能を改善
window.addEventListener('scroll', handleScroll, { passive: true });
ポイント:{ passive: true } を指定すると、ブラウザに「このハンドラは preventDefault() を呼ばない」と伝えるため、スクロールのパフォーマンスが向上します。スクロールを監視するだけの場合は常に指定しましょう。
まとめ
JavaScriptでスクロール位置を制御する方法を、3つのメソッドと実務パターンに分けて解説しました。最後に、各メソッドの特徴を比較表でまとめます。
| メソッド |
スクロール方式 |
スムーズ対応 |
主な用途 |
| window.scrollTo() |
絶対位置(座標指定) |
behavior: smooth |
トップに戻る、特定座標へ移動 |
| Element.scrollIntoView() |
要素指定 |
behavior: smooth |
特定要素を画面内に表示 |
| window.scrollBy() |
相対位置(差分指定) |
behavior: smooth |
現在位置から一定量移動 |
| scroll-behavior CSS |
ページ全体 |
CSS指定 |
アンカーリンクのスムーズ化 |
実装の選び方まとめ
- ページ全体のスムーズスクロール → CSSの
scroll-behavior: smooth が最もシンプル
- 特定の要素を表示したい →
scrollIntoView() が座標計算不要で便利
- 固定ヘッダーがある → CSSの
scroll-margin-top で余白を確保
- 座標を細かく制御したい →
scrollTo() + getBoundingClientRect()
- SPA → ルーターの設定でスクロール位置をリセット
- アクセシビリティ →
prefers-reduced-motion を考慮する
スクロール制御はWeb開発で頻出するテクニックです。この記事で紹介した3つのメソッドの違いを理解し、実装シーンに応じて最適な方法を選択してください。特に scrollIntoView() と scroll-margin-top の組み合わせは、シンプルかつ確実なので最もおすすめです。