【jQuery/JS】ページ離脱時にアラートを表示する完全ガイド|beforeunload・フォーム未保存・条件付き発火・解除まで

フォームへの入力途中にうっかりページを閉じてしまった経験は誰でもあります。beforeunload イベントを使えば、ページを離れる直前にブラウザの確認ダイアログを表示し、未保存データの損失を防ぐことができます。この記事では基本的な実装から、フォーム変更時のみ発火させる方法・解除方法・注意点まで解説します。

この記事でわかること

  • beforeunload イベントの基本的な使い方
  • フォームが変更された場合のみアラートを表示する
  • 保存完了後にアラートを解除する
  • モダンブラウザでのカスタムメッセージの制限
  • 特定リンク(同一ドメイン)では発火させない例外処理
スポンサーリンク

基本: beforeunloadイベントでページ離脱を検知する

beforeunload イベントはページを閉じる・別URLに遷移する・リロードする直前に発火します。イベントハンドラ内で event.preventDefault() を呼ぶことで確認ダイアログが表示されます。

$(function () {
  $(window).on('beforeunload', function (e) {
    // preventDefault() で確認ダイアログを表示する
    e.preventDefault();
    // 一部の古いブラウザ対応で returnValue も設定する
    e.returnValue = '';
  });
});
カスタムメッセージは表示されない(2024年以降)
かつては return "ページを離れますか?" でカスタムメッセージが表示されていましたが、現在の主要ブラウザ(Chrome・Firefox・Safari・Edge)はカスタムメッセージを無視し、ブラウザ標準のメッセージのみ表示します。これはフィッシング対策として意図的な仕様変更です。

フォームが変更された場合のみアラートを表示する(推奨パターン)

常にアラートが出るとユーザーにとって煩わしくなります。フォームが実際に変更された場合のみアラートを出す実装が推奨です。

$(function () {
  var isDirty = false; // フォームが変更されたかのフラグ

  // フォームの入力・変更を検知
  $('form').on('input change', function () {
    isDirty = true;
  });

  // ページ離脱時: 未保存の変更がある場合のみ確認
  $(window).on('beforeunload', function (e) {
    if (isDirty) {
      e.preventDefault();
      e.returnValue = '';
    }
  });

  // 保存ボタンクリック時: フラグをリセット(送信成功後)
  $('form').on('submit', function () {
    isDirty = false; // 送信したので離脱確認不要
  });
});
isDirtyフラグで制御するのがベストプラクティス
フォームの変更有無をフラグ変数で管理することで、「入力した内容がある場合のみ警告する」という自然なUXが実現できます。保存完了・送信後はフラグを false に戻すことを忘れずに。

保存完了後・特定の操作でアラートを解除する

$(function () {
  var isDirty = false;
  var handler = function (e) {
    e.preventDefault();
    e.returnValue = '';
  };

  $('form').on('input change', function () {
    if (!isDirty) {
      isDirty = true;
      // ハンドラーを登録
      $(window).on('beforeunload', handler);
    }
  });

  // 保存ボタンクリック後: ハンドラーを削除して解除
  $('#save-btn').on('click', function () {
    isDirty = false;
    $(window).off('beforeunload', handler); // ハンドラーを解除
  });

  $('form').on('submit', function () {
    isDirty = false;
    $(window).off('beforeunload', handler);
  });
});
off() で確実に解除する
ハンドラーを名前付き関数(変数)として定義しておくことで、$(window).off("beforeunload", handler) で特定のハンドラーだけを解除できます。$(window).off("beforeunload") はすべての beforeunload ハンドラーを削除するので注意してください。

Ajaxで自動保存している場合

入力内容を一定間隔でAjax自動保存している場合は、最後の保存以降に変更があった場合のみアラートを出します。

$(function () {
  var isDirty = false;

  $('form').on('input change', function () {
    isDirty = true;
  });

  // 30秒ごとに自動保存
  setInterval(function () {
    if (!isDirty) return;

    $.ajax({
      url: '/api/autosave',
      method: 'POST',
      data: $('form').serialize(),
      success: function () {
        isDirty = false; // 保存完了でフラグをリセット
        console.log('自動保存完了');
      }
    });
  }, 30000);

  $(window).on('beforeunload', function (e) {
    if (isDirty) {
      e.preventDefault();
      e.returnValue = '';
    }
  });
});

使用上の注意点と制限事項

注意事項 詳細
カスタムメッセージは無効 主要ブラウザはカスタムメッセージを表示しない。ブラウザ標準のメッセージのみ表示
モバイルブラウザ iOS Safariなど一部のモバイルブラウザでは beforeunload が発火しない場合がある
UXへの影響 常にアラートが出るとユーザーが煩わしく感じる。条件付きで発火させること
SEO影響なし JavaScriptイベントのためGooglebotには影響しない
ユーザーが無視できる 「このページを離れる」を選べばそのまま離脱できる。完全な阻止はできない

まとめ

beforeunload イベントを使ったページ離脱アラートの実装ポイントをまとめます。

  • 基本: e.preventDefault(); e.returnValue = ""; で確認ダイアログを表示
  • 推奨: isDirty フラグで「フォーム変更時のみ」発火させる
  • 解除: 保存・送信後は isDirty = false + $(window).off("beforeunload", handler)
  • カスタムメッセージは現代ブラウザでは表示できない(ブラウザ標準文言のみ)

関連記事: フォームバリデーション完全ガイド / ページがロードされたときに処理を実行する方法 / POSTリクエストを送信する方法

よくある質問(FAQ)

Qカスタムメッセージを表示することはできますか?
Aできません。Chrome・Firefox・Safari・Edgeなどの主要ブラウザはフィッシング対策としてカスタムメッセージを無視し、ブラウザ標準の文言のみを表示します。これは仕様変更であり、回避方法はありません。
Q同一ドメイン内のページ遷移では発火させたくないです。
Abeforeunloadは遷移先に関わらず発火します。同一ドメイン内のリンクをクリックした場合は発火させたくない場合は、リンクの click イベントで isDirty = false にしてから遷移させる方法があります。ただし Ajaxページ遷移(SPA)では beforeunload は発火しません。
Qスマホで動作しません。
AiOS Safariはbeforeunloadイベントの対応が不完全で、発火しない場合があります。ページの非表示を検知するには document.addEventListener("visibilitychange")window.addEventListener("pagehide") の方がより安定して動作します。
Qフォームをリセット(初期状態に戻す)した場合もアラートが出ます。
Ainput/changeイベントが発火するたびに isDirty = true にするだけではリセット後も true のままです。より厳密に管理するには、フォームの初期値を保存しておき、現在値と比較して変更がなければ isDirty = false に戻す処理を追加してください。
QWordPressのGutenbergエディターで使えますか?
AWordPressのブロックエディター(Gutenberg)は独自の未保存変更検知機能を持っており、独自の beforeunload ハンドラーが登録されています。フロントエンドのフォームページに独自実装する場合は問題ありませんが、管理画面での実装はGutenbergの動作と競合する可能性があるため注意してください。