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

beforeunload とは?

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

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

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

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

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

ダイアログの表示例

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

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

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

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

JavaScript が無効になっている

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

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

<body onbeforeunload="return 'TEST'">

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

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

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

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

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

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

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()などのメソッドが無視(ダイアログを表示しない)されます。

参考