🧊

beforeunloadイベントと確認ダイアログ

Changes you made may not be saved.

フォームの入力中とかにページを離脱しようと出るアレのこと。

昔から微妙に使われてるくせに、いまいち挙動が不定でブラウザ差異もあって釈然としない。

最近はどうなってるのか知る必要が出たので、改めて調べた。

基本のかたち

コードとしてはこう書けばよい。

window.addEventListener("beforeunload", (ev) => {
  // unloadをキャンセル = 本当に離れるのかを確認する
  ev.preventDefault();
});

イベントハンドラ内で、`preventDefault()`すれば、非同期でダイアログが出せる。
と、Specにも書いてある。

ただし、ブラウザによってはそれだけではダメなやつがある。

Chrome

それがまさかのChrome

Chromeの場合はこう。

window.addEventListener("beforeunload", (ev) => {
  // なくてもいい
  // ev.preventDefault();
  ev.returnValue = "";
});

という具合に、イベントオブジェクトの`returnValue`に文字列を代入する必要がある。
空文字列でもいいし、何かしらの文字列でもいいけど、指定したとしても、確認ダイアログにそれが出るわけではない。

Firefox / Safari

冒頭の基本のやつで問題なし。

まとめ

window.addEventListener("beforeunload", (ev) => {
  if (shouldUnload) return;

  ev.preventDefault();
  ev.returnValue = "";
});

特定の条件でだけ引き止めたい時は、こんな風に変数を見るようにして、`shouldUnload`を`false`にすればよい。

あと、独自のUIでの引き止めは、現状どうやってもできない。
確認ダイアログの文言はいじれないし、ダイアログ出さずに遷移をキャンセルする方法もなさそう。

あとは、単にリロードしただけのような場合(ユーザーアクションがなにもない場合)は、確認ダイアログが表示されないようになってる。(ブラウザの粋な計らいっぽい)

あとあと、遠い記憶でモバイルでは`beforeunload`が発火しないとかあった気がする。