beforeunload のダイアログが出現しないことがある

beforeunload とは?

beforeunloadイベントは、ページがアンロードされる直前に発生します。beforeunloadイベントが発生時は、ページがまだ表示されており、イベントもキャンセル可能です。

beforeunloadイベントは、ページにダイアログを表示し、ユーザーにページを閉じるか確認を求めることができます。ユーザーが確認すれば、ブラウザは新しいページへ遷移し、そうでなければ遷移をキャンセルします。(ユーザーの確認なしにページ遷移をキャンセルすることはできません)

サンプル(確認ダイアログを表示する)

beforeunload.html<script>
window.addEventListener('beforeunload', function(event) {
  event.preventDefault();
  event.returnValue = '';
});
</script>

※ HTML の仕様では、event.preventDefault();を使用する必要があります。
 ただし、 Chrome ではevent.returnValue = '';を設定する必要があります。
 そのため、 Chrome系とそれ以外に対応するため、両方の記載を併記しています。
returnValueに空文字以外の値を設定するとユーザー確認に使用されます。
 ただし、これは歴史的な機能であり現在は値を設定しても定型文が使用されます。
 Remove Custom Messages in onbeforeload Dialogs after Chrome 51

ダイアログの表示例

Chrome
「このサイトを離れますか?」
「行った変更が保存されない可能性があります。」
「このページを離れる」「キャンセル」

Firefox
「(host)」
「このページから移動しますか? 入力した情報は保存されません。」
「このページから移動する」「このページに留まる」

ダイアログが出現しないことがある

上記のサンプルを使用してもダイアログが出現しないことがあります。

JavaScript が無効になっているため

beforeunloadイベントの登録には JavaScript の実行が必要です。そのため、 JavaScript 無効環境ではダイアログが出現しません。

また、下記のようにタグに onbeforeunload を記述する方法でも内部的には JavaScript が使用されているため、 JavaScript 無効環境ではダイアログが出現しません。

<body onbeforeunload="return 'TEST'">

ブラウザが対応していないため

Safari on iOS / WebView Android などの一部のブラウザは、beforeunloadイベントのダイアログ表示(event.preventDefault() / event.returnValue)に対応していません。そのため、ダイアログ出現させることができません。


また、 Firefox の about:config 設定で次の変更がある場合、beforeunloadイベントのダイアログ表示が無効化されます。ただし、この設定は初期値でダイアログ表示が有効となっています。

dom.disable_beforeunload
false → true

ページとユーザーの対話が存在しないため

ページを表示後にユーザーとの対話が存在しない場合(ジェスチャ操作が存在しない場合)、ダイアログは出現しません。元々、ページに入力した情報を保護する機能であるため、ユーザーによる入力が存在しないのであれば、保護する必要もありません。

ユーザーとの対話は、ユーザーイベントだと考えられます。FID(First Input Delay:初回ユーザーイベント)などでは、click / mousedown / keydown / touchstart / pointerdownをユーザーイベントとして扱います。初回ユーザーイベントが発生していない状態ではユーザーとの対話がないものと考えられます。

Element.click()等で JavaScript から意図的にイベントを発生させたとしてもそれは、ユーザーイベントとしてはカウントされない点に注意してください。また、ユーザーイベントに scroll / mousemove を含まない点も考慮してください。

そしてまた、window.alert() / window.confirm() / window.prompt() ダイアログへのユーザー入力と確認も考慮されません。window.prompt()でデータを入力しても他のユーザー操作がなしであればダイアログは出現しません。(これは仕様バグなのでは?)


ユーザーイベントがある場合、ダイアログを表示しますが、ユーザーイベントがない場合、ダイアログを表示せず、デベロッパーツールに次のエラーを出力します。デベロッパーツールにエラーを出力したくない場合、ユーザーイベント発生後にbeforeunloadを登録する方法などが考えられます。

Chrome
[Intervention] Blocked attempt to show a 'beforeunload' confirmation panel for a frame that never had a user gesture since its load. https://www.chromestatus.com/feature/5082396709879808

意図的にダイアログを出現させない

beforeunloadイベントのダイアログは、ユーザーにとって常に有益とは限りません。この機能は、ときに邪魔に思うユーザーがいるかもしれません。次のユーザースクリプトで問題を解決できるかもしれません。

// 他の beforeunload イベントへのイベント伝搬を阻害します
// このコードより早く window のキャプチャフェーズへイベント登録された場合、効果がありません
window.addEventListener('beforeunload', function(event) {
  event.stopImmediatePropagation();
}, true);

// 既に登録済みの window.onbeforeunload を除去します
window.onbeforeunload = function(event) {};

※上記のコードを常に使用することはおすすめできません。
 下記の「備考(キャッシュの不使用)」のため、ブラウザ体験が劣化します。
 導入するのであれば、特定のページ(サイト)に限定すべきです。

備考(キャッシュの不使用)

beforeunload / unloadイベントが設定されている場合、ブラウザは bfcache (Back Forward Cache) or WebKit のページキャッシュを使用しなくなります。

キャッシュの使用により、ユーザー体験が向上します。不必要に beforeunload / unload を使用せずに pagehide の使用を検討してください。

備考(beforeunload 内では、ダイアログを表示できない)

beforeunloadイベント中は、window.alert() / window.confirm() / window.prompt()などのメソッドが無視(ダイアログを表示しない)されます。

参考