【JavaScript】特定の位置までスクロールしたら自動でページ遷移させる方法|scrollY閾値・要素到達・IntersectionObserver・遅延遷移・UX配慮まで解説

「ページの最下部まで読んだら次のコンテンツへ自動遷移」「CTA要素が画面内に入ったら詳細ページへ誘導」など、スクロール位置をトリガーにしたページ遷移はコンテンツ誘導やシングルページ型体験の演出に使われます。

この記事ではスクロール量(scrollY)による閾値判定、要素への到達を検知するIntersectionObserver方式、一度だけ発火する仕組み、遅延カウントダウン遷移、ユーザーへの通知UIとキャンセル対応まで体系的に解説します。

スポンサーリンク

基本:scrollY で閾値を判定してページ遷移

window.scrollY がある値を超えたら location.href でページを遷移させる、最もシンプルな実装です。

scrollY 閾値で遷移(基本形)
window.addEventListener('scroll', () => {
  // ページ上部から500px スクロールしたら遷移
  if (window.scrollY >= 500) {
    window.location.href = 'https://example.com/next-page/';
  }
});
この実装には重大な問題があります:

  1. scroll イベントは高頻度で発火するため、条件を満たすたびに何度もリダイレクトが試みられます(ブラウザは最初の1回で遷移しますが無駄な処理が発生)
  2. スクロールするたびにチェックが走り続けるためパフォーマンスに影響します
  3. 一度遷移が始まったらキャンセルできません

次のセクションでこれらを解決した実装を紹介します。

改善版:一度だけ発火させる

フラグ変数または { once: true } オプションを使い、条件を満たした後はリスナーを解除します。

一度だけ発火するスクロール遷移
function setupScrollRedirect(threshold, url) {
  function onScroll() {
    if (window.scrollY >= threshold) {
      // リスナーを削除して以降は発火させない
      window.removeEventListener('scroll', onScroll);
      window.location.href = url;
    }
  }
  window.addEventListener('scroll', onScroll, { passive: true });
}

// ページを500px スクロールしたら遷移
setupScrollRedirect(500, 'https://example.com/next-page/');
{ passive: true } を指定する理由:scroll イベントリスナーに passive: true を指定すると、ブラウザがスクロールのデフォルト動作をブロックしないと判断してスクロール処理を最適化できます。遷移処理は preventDefault() を呼ばないので、必ず passive: true を付けましょう。

ページ末尾まで読んだら遷移

「記事を最後まで読んだ」タイミングを検知するには、現在のスクロール位置と全体の高さを比較します。

ページ末尾到達を検知して遷移
function setupBottomRedirect(url, marginPx = 100) {
  function onScroll() {
    const scrolled    = window.scrollY + window.innerHeight;
    const totalHeight = document.documentElement.scrollHeight;

    // 末尾から marginPx 以内に到達したら遷移
    if (scrolled >= totalHeight - marginPx) {
      window.removeEventListener('scroll', onScroll);
      window.location.href = url;
    }
  }
  window.addEventListener('scroll', onScroll, { passive: true });
}

setupBottomRedirect('https://example.com/next-article/', 100);
各値の意味:

  • window.scrollY:ページ上部からスクロールした量(px)
  • window.innerHeight:ビューポートの高さ(px)
  • document.documentElement.scrollHeight:ページ全体の高さ(px)
  • scrollY + innerHeight:現在見えている範囲の下端の位置

IntersectionObserver で特定要素への到達を検知

「フッターが画面内に入ったら遷移」「CTA要素が表示されたら遷移」など、特定の要素が画面に入ったタイミングで遷移させたい場合は IntersectionObserver が最も適切です。scroll イベントより効率的で、閾値計算も不要です。

IntersectionObserver で要素到達を検知して遷移
/**
 * 指定した要素が画面内に入ったらページ遷移する
 * @param {string} selector  監視する要素のセレクター
 * @param {string} url       遷移先 URL
 * @param {number} threshold 0〜1(要素の何割が見えたら発火するか)
 */
function setupElementRedirect(selector, url, threshold = 0.5) {
  const target = document.querySelector(selector);
  if (!target) return;

  const observer = new IntersectionObserver((entries) => {
    const entry = entries[0];
    if (entry.isIntersecting) {
      observer.disconnect(); // 一度だけ発火して監視終了
      window.location.href = url;
    }
  }, { threshold });

  observer.observe(target);
}

// フッターが50%以上見えたら遷移
setupElementRedirect('#page-footer', 'https://example.com/next/', 0.5);

// 記事末尾の「次の記事へ」要素が完全に表示されたら遷移
setupElementRedirect('#next-article-trigger', 'https://example.com/article/2/', 1.0);
IntersectionObserver が scroll イベントより優れている理由:scroll イベントはスクロールのたびに毎フレーム発火しますが、IntersectionObserver はブラウザが要素の可視状態が変化したときだけコールバックを呼ぶため、CPU負荷が大幅に低下します。スクロールイベントの使い方についてはIntersectionObserverを使ったスクロールイベントの設定方法も参考にしてください。

遅延遷移:カウントダウンUIとキャンセル対応

突然のページ遷移はユーザーを驚かせます。実務では「3秒後に自動遷移します」のようなカウントダウンUIと、キャンセルボタンを提供するのがUXのベストプラクティスです。

カウントダウンUIの HTML
<!-- 遷移通知バナー(通常は非表示) -->
<div id="redirect-banner" hidden style="
  position: fixed; bottom: 0; left: 0; right: 0;
  background: #1e40af; color: white;
  padding: 12px 20px; display: flex;
  justify-content: space-between; align-items: center;
  font-size: 14px; z-index: 9999;
">
  <span id="redirect-message">3秒後に次のページへ移動します…</span>
  <button id="cancel-redirect" style="
    background: white; color: #1e40af;
    border: none; padding: 6px 16px;
    border-radius: 4px; cursor: pointer; font-weight: bold;
  ">キャンセル</button>
</div>
カウントダウン付き遅延遷移の実装
/**
 * スクロールトリガー + カウントダウン + キャンセル可能な遷移
 */
function setupCountdownRedirect({ selector, url, delay = 3, threshold = 0.5 }) {
  const target  = document.querySelector(selector);
  const banner  = document.getElementById('redirect-banner');
  const message = document.getElementById('redirect-message');
  const cancelBtn = document.getElementById('cancel-redirect');

  if (!target || !banner) return;

  let timerId   = null;
  let cancelled = false;

  function startCountdown() {
    let remaining = delay;
    banner.hidden = false;
    message.textContent = `${remaining}秒後に次のページへ移動します…`;

    timerId = setInterval(() => {
      remaining--;
      if (remaining <= 0) {
        clearInterval(timerId);
        if (!cancelled) window.location.href = url;
      } else {
        message.textContent = `${remaining}秒後に次のページへ移動します…`;
      }
    }, 1000);
  }

  // キャンセルボタン
  cancelBtn.addEventListener('click', () => {
    cancelled = true;
    clearInterval(timerId);
    banner.hidden = true;
    observer.disconnect(); // 再発火させない
  });

  // IntersectionObserver で要素到達を監視
  const observer = new IntersectionObserver((entries) => {
    if (entries[0].isIntersecting) {
      observer.disconnect();
      startCountdown();
    }
  }, { threshold });

  observer.observe(target);
}

// 使用例
setupCountdownRedirect({
  selector:  '#article-end',
  url:       'https://example.com/next-article/',
  delay:     5,       // 5秒後に遷移
  threshold: 0.8,
});
aria 属性でアクセシビリティを強化する場合:バナーに role="alert"aria-live="assertive" を付けると、スクリーンリーダーが「3秒後に遷移します」とユーザーに読み上げます。キャンセルボタンは autofocus を付けてキーボード操作できるようにしましょう。

location.href と location.replace の使い分け

URLを変える方法が複数あります。「戻るボタンで元のページに戻れるようにするか」が選択の基準です。

遷移方法の比較
// ① location.href(推奨)
// 履歴に残るので「戻る」ボタンで元ページに戻れる
window.location.href = 'https://example.com/next/';

// ② location.replace
// 履歴に残らない。「戻る」で元ページには戻れない
// 自動ログインリダイレクトなど「戻られたくない」場合に使う
window.location.replace('https://example.com/next/');

// ③ history.pushState(SPA用)
// URL は変わるがページ遷移しない。SPA でのルーティングに使う
history.pushState({}, '', '/next-page');
方法 「戻る」ボタン ページリロード 用途
location.href 元ページに戻れる あり 一般的な遷移
location.replace 戻れない あり リダイレクト・ログイン後
history.pushState 戻れる(SPA内) なし SPA ルーティング
URL 遷移の詳しい方法についてはJavaScriptでURLを遷移させる方法で網羅的に解説しています。

スクロール量をパーセントで計算して遷移

「ページの80%を読んだら遷移」のように、絶対的なpx値ではなくパーセントで閾値を設定できます。

スクロール率(%)で閾値を設定
/**
 * スクロール量のパーセントでリダイレクトをトリガー
 * @param {number} percent 0〜100(例: 80 = 80%スクロールしたら)
 * @param {string} url     遷移先 URL
 */
function setupPercentRedirect(percent, url) {
  function onScroll() {
    const scrolled = window.scrollY + window.innerHeight;
    const total    = document.documentElement.scrollHeight;
    const ratio    = (scrolled / total) * 100;

    if (ratio >= percent) {
      window.removeEventListener('scroll', onScroll);
      window.location.href = url;
    }
  }
  window.addEventListener('scroll', onScroll, { passive: true });
}

// ページの80%をスクロールしたら遷移
setupPercentRedirect(80, 'https://example.com/next/');

scroll イベントのデバウンスでパフォーマンス改善

scroll イベントは1秒間に数十〜数百回発火します。複雑な計算をする場合はデバウンス(一定時間内の連続呼び出しをまとめる)を使います。

デバウンス付きスクロール遷移
function debounce(fn, wait) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), wait);
  };
}

const checkScroll = debounce(() => {
  if (window.scrollY >= 500) {
    window.location.href = 'https://example.com/next/';
  }
}, 50); // 50ms ごとに1回だけ実行

window.addEventListener('scroll', checkScroll, { passive: true });
デバウンスの注意:デバウンスの待機時間(wait)を大きくしすぎると、ユーザーがスクロールを止めてから遷移するまでに遅れが生じます。スクロール遷移には 50〜100ms 程度が適切です。より大きな待機時間が必要な場合は IntersectionObserver の使用を検討してください。

よくある質問(FAQ)

QscrollY と pageYOffset の違いは何ですか?
A機能的には同じです。window.pageYOffset は古い名称で、現在は window.scrollY が標準です。IE 非対応で問題なければ scrollY を使いましょう。
Qスクロールトリガーの遷移で「戻る」ボタンを押すとまた遷移してしまいます。どう防ぐ?
AsessionStorage にフラグを保存して、一度遷移が発火したら再発火しないようにします。
if (sessionStorage.getItem("redirected")) return;
遷移前に sessionStorage.setItem("redirected", "1") を実行しておけば、戻ってきても再遷移しません。
Qスクロール位置ではなく「x秒間そのページにいたら遷移」にするには?
AsetTimeout で指定秒数後に遷移させます。ユーザーが操作したかどうかを監視するには一定時間スクロールしなかったらポップアップを表示する方法の実装パターンが参考になります。
Qモバイルで scrollY がうまく取得できません。
AiOS Safari では document.documentElement.scrollTop0 を返す場合があります。window.scrollY ?? document.documentElement.scrollTop ?? document.body.scrollTop のようにフォールバックするか、IntersectionObserver を使うとブラウザ差異を気にせず実装できます。
Q自動遷移はSEOに影響しますか?
AGooglebot はJavaScriptを実行しますが、スクロールをシミュレートしないため自動遷移は発火しません。SEO 上は問題ありませんが、ユーザーへの配慮(カウントダウン表示・キャンセル機能)がUXとして重要です。突然の自動遷移はユーザーを混乱させ、直帰率の上昇につながります。

まとめ

スクロールトリガーのページ遷移実装を用途別にまとめます。

用途 推奨手法
px 単位の閾値で遷移 scroll イベント + scrollY 比較 + リスナー削除
ページ末尾到達で遷移 scrollY + innerHeight >= scrollHeight - margin
特定要素が画面内に入ったら遷移 IntersectionObserver(推奨・高効率)
カウントダウン + キャンセル付き遷移 setInterval カウントダウン + キャンセルボタン
スクロール率(%)で遷移 (scrollY + innerHeight) / scrollHeight * 100
「戻る」で再遷移を防ぐ sessionStorage にフラグ保存

スクロールイベントの監視についてはIntersectionObserverでスクロールイベントを設定する方法を、URL遷移の方法全般についてはJavaScriptでURLを遷移させる方法もあわせて参照してください。