[33] [DFN[[CODE(DOMe)@en[[[beforeunload]]]]]] は、[[navigate]] や[[閲覧文脈を閉じる]]ために[[文書]]が[[活性文書]]でなくなろうとしていることを知らせる[[DOMイベント]]です。

[34] [[スクリプト]]は、この[[イベント]]を使って、必要があれば[[利用者]]に対して本当に処理を続行するかどうか[[利用者エージェント]]に確認させることができます。

[EG[
[35] 例えば[[文書]]を編集する [[Webアプリケーション]]では、変更がすべて[[サーバー]]上に保存されていない時に、
そのまま遷移または閉じて変更を破棄しても良いかを[[利用者]]に尋ねるために本[[イベント]]を使うことができます。
]EG]

* 仕様書

[REFS[
- [6] '''[CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-04-25 04:40:19 +09:00]] 版) <https://html.spec.whatwg.org/#prompt-to-unload-a-document>'''
-- [13] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-04-25 04:40:19 +09:00]] 版) <https://html.spec.whatwg.org/#beforeunloadevent>
]REFS]

* 確認ダイアログ

[29] [CODE(DOMe)@en[[[beforeunload]]]] [[イベント]]で文字列が指定されたか[[イベント]]が[[取り消し]]された場合には、
[[利用者]]に対して unload を続行するかどうか確認する[[モーダルダイアログ]]が (原則として)
表示されます。

[30] このダイアログには、
[[利用者]]の可能な選択肢として続行するボタンと続行しないボタンが表示されます。

[HISTORY[
[45] かつては[[著者]]が指定した文字列があれば、それを表示することになっていました。
しかし悪意ある[[著者]]が[[利用者]]を欺くために使うことがあるので、
この機能は廃止されました。
]HISTORY]

[31] [[termination nesting level]] が設定される (>>32) ため、
[CODE(HTMLe)@en[[[beforeunload]]]] 中では [CODE(DOMe)@en[[[confirm]]]]
は使うことができず、こちらの方法で間接的に[[モーダルダイアログ]]を表示させる必要があります。

;; [36] 仕様上は[[モーダルダイアログ]]を表示するべきことは [MUST[MUST]] ではなく
[SHOULD[SHOULD]] となっていますが、[[利用者インターフェイス]]に関する要件であることや、
[[利用者]]に対する [[DoS攻撃]]に乱用される可能性も否定できないことなどを踏まえて表示しない余地を与えているものと思われます。
そのような特殊な状況や、仕様上除外されている[[砂箱化]]された状態を除いて、
[[モーダルダイアログ]]を表示しないような実装は [[Web互換]]ではありません。

[43] 表示については[[モーダルダイアログ]]も参照。

* 処理

[7] [DFN[[[prompt to unload a document]]]] は、
[VAR[文書]]と[VAR[再帰フラグ]]について、
次のようにしなければ[MUST[なりません]] [SRC[>>6]]。

[FIG(steps)[
= [32] [VAR[文書]]の[[イベントループ]]の [[termination nesting level]] を
[N[1]] [[インクリメント]]します。
= [54] [VAR[文書]]の [[ignore-opens-during-unload counter]] を
[N[1]] [[インクリメント]]します。
= [55] [VAR[イベント]]を、 [CODE(DOMi)@en[BeforeUnloadEvent]] [[イベントを作成]]した結果に設定します。
= [56] [VAR[イベント]]の[F[型][イベント型]]を、 [CODE(DOMe)@en[beforeunload]] に設定します。
= [57] [VAR[イベント]]の[F[取り消し可能]]を、[[真]]に設定します。
= [58] [VAR[イベント]]を [[dispatch]] します。
[FIG(list members)[
: [VAR[[[対象][イベント対象]]]] : [VAR[文書]]の [CODE(DOMi)@en[Window]]
]FIG]
= [63] [VAR[文書]]の[[イベントループ]]の [[termination nesting level]] を
[N[1]] [[デクリメント]]します。
= [64] [VAR[イベント]]の[[dispatch]]でいずれかの[[イベントリスナー]]が実行されていたらなら、
== [65] [VAR[文書]]の [F[''salvageable'']] を、[[偽]]に設定します。
= [72] [DFN[[VAR[[[refused to allow the document to be unloaded]]]]]] を、[[偽]]に設定します。
= [26] 
[FIG(list)[
- [28] [VAR[文書]]の[[活性砂箱化フラグ集合]]の [[sandboxed modals flag]] が設定されていない
- [27] [VAR[イベント]]の [F[[CODE(DOMa)@en[returnValue]]]]
が[[空文字列]]以外であるか、[VAR[イベント]]が[F[取り消し]]されている
- [48] [[利用者エージェント]]が適切と判断した
]FIG]
... の'''すべて'''の条件を満たすなら、
== [49] [[利用者]]に対して unload を望むか確認します。
== [40] [[利用者]]の応答があるまで、 [[pause][pause (event loop)]] します。
== [41] [[利用者]]が unload を望まないと応答したら、
=== [50] [VAR[refused to allow the document to be unloaded]] を、[[真]]に設定します。
= [71] [VAR[再帰]]が[[偽]]の場合、
== [66] [VAR[文書]]の[[子孫閲覧文脈のリスト]]の各[VAR[閲覧文脈]]について、
=== [73] [VAR[結果]]を、[[prompt to unload]] を実行した結果に設定します。
[FIG(list members)[
: [VAR[文書]]: [VAR[閲覧文脈]]の[F[活性文書]]
: [VAR[再帰フラグ]] : [[真]]
]FIG]
=== [74] [VAR[結果]]が[[真]]の場合、
==== [75] [VAR[refused to allow the document to be unloaded]] を、[[真]]に設定します。
==== [76] [VAR[閲覧文脈]]の[[ループ]]を抜けます。
=== [67] [[閲覧文脈]]の[F[活性文書]]の [F[''salvageable'']] が[[偽]]の場合、
==== [68] [VAR[文書]]の [F[''salvageable'']] を、[[偽]]に設定します。
= [69] [VAR[文書]]の [F[ignore-opens-during-unload counter]] を
[N[1]] [[デクリメント]]します。
= [70] [VAR[refused to allow the document to be unloaded]] を返します。
]FIG]

[51] [[利用者]]に確認するかどうかの判断は[[利用者エージェント]]に委ねられていますが、
例えば、[[利用者]]が当該[[閲覧文脈]]と[[対話]]していない状態なら、
省くことができます [SRC[>>13]]。裏の[[タブ]]で勝手に [[navigate]]
が発生した場合などが該当しそうです。

;; [42] [[利用者]]に対する確認の表示については、[[モーダルダイアログ]]を参照。

[8] [[prompt to unload a document]] は [[navigate]] から呼び出されます。

;; [10] [[素片識別子へのスクロール]]しかしない場合は呼び出されませんが、
[[fetch]] を行う場合などは (最終的に[[ダウンロード]]などに至るとしても、
それが決まる前に) 呼び出されます。詳しくは [[navigate]] を参照。

[9] [[prompt to unload a document]] の実行中は、 [[navigate]] は何もしません。
つまり [CODE(DOMe)@en[[[beforeunload]]]] [[イベント]]内ではページ遷移の開始が抑制されます。

;; [[navigate]] を参照。なお ⊿ を指定した[[履歴の探索]]は抑制されませんが、
実際の処理は別の[[タスク]]を[[タスクキュー]]に入れる形で実行されるので、
[CODE(DOMe)@en[[[beforeunload]]]] [[イベント]]内で同期的にページ遷移が開始されることはありません。

[11] [[prompt to unload a document]] は ⊿ を指定した[[履歴の探索]]や、
[CODE(JS)@en[[[window.open]]]] や、[[閲覧文脈を閉じる]]際にも呼び出されます。

[12] [[prompt to unload a document]] が呼び出された場合、 refuse
されていなければ、その後 [[unload a document]] が呼び出されることになります。

* [CODE(DOMi)@en[BeforeUnloadEvent]] インターフェイス

[15] [DFN[[CODE(DOMi)@en[[[BeforeUnloadEvent]]]]]] [[インターフェイス]] [SRC[>>13]]
は、 [CODE(DOMe)@en[[[beforeunload]]]] [[イベント]]で使われます。

[17] [CODE(DOMi)@en[[[BeforeUnloadEvent]]]] [[インターフェイス]]は、
[[文書環境]]に[[晒され]]ています [SRC[>>13]]。

[16] [CODE(DOMi)@en[[[BeforeUnloadEvent]]]] [[インターフェイス]]は、
[CODE(DOMi)@en[[[Event]]]] [[インターフェイス]]を[[継承]]しています [SRC[>>13]]。

;; [18] 他の[[イベント]]インターフェイスとは違って、[[コンストラクター]]はありません。

[19] [CODE(DOMi)@en[[[BeforeUnloadEvent]]]] [[インターフェイス]]は、
[DFN[[CODE(DOMa)@en[[[returnValue]]]]]] [[IDL属性]]を持ちます [SRC[>>13]]。

;; 他の[[イベント]]インターフェイスの[[属性]]とは違って、読み書きの両方が可能です。

[20] [CODE(DOMa)@en[[[returnValue]]]] [[IDL属性]]は、 [CODE(DOMi)@en[[[DOMString]]]]
を設定、取得できる[[属性]]で、初期値は[[空文字列]]です [SRC[>>13]]。
[[イベントリスナー]]内で[[著者]]が適切な値を設定することが想定されていました。

[46] この[[IDL属性]]は、[[後方互換性]]のためのものです [SRC[>>13]]。
値が設定されているかどうかが処理に反映されますが、値そのものは無視されます。
[[著者]]は、 [CODE(DOMm)@en[preventDefault]] などを使う[SHOULD[べきです]] [SRC[>>13]]。

[HISTORY[
[21] [[prompt to unload a document]] は、
[[著者]]が [CODE(DOMa)@en[[[returnValue]]]] に非[[空文字列]]を設定したら、
それを[[モーダルダイアログ]]に表示することになっていました。

[22] [[IEのイベントモデル]]では、[[イベントハンドラー]]の[[返り値]]は[[イベント]]オブジェクトの
[CODE(DOMa)@en[[[returnValue]]]] を設定するのと同義でした。現在の [[DOM]]
では、唯一 [CODE(DOMi)@en[[[BeforeUnloadEvent]]]] にのみ [CODE(DOMa)@en[[[returnValue]]]]
が残っています。
]HISTORY]

* [CODE(DOMa)@en[onbeforeunload]] 属性

[14] [CODE(DOMa)@en[[[onbeforeunload]]]] [[イベントハンドラー属性]]は、
他の[[イベントハンドラー]]とは違って、[[返り値]]が[[イベント]]オブジェクトの
[CODE(DOMa)@en[[[returnValue]]]] [[IDL属性]]の値 (を[[文字列化]]したもの) に設定されます。

;; [[イベントハンドラー]]参照。

* 歴史

[REFS[
- [62] [CITE@en['''['''whatwg''']''' salvaging work while navigating away from a web app -- onunload="confirm('save before quitting?')]] ([[Ian Hickson]]著, [TIME[2008-12-12 17:55:08 +09:00]]) <https://lists.w3.org/Archives/Public/public-whatwg-archive/2008Dec/0146.html>
--[1] [CITE[Re: salvaging work while navigating away from a web app -- onunload="confirm('save before quitting?')]] ([[Ian Hickson]] 著, [TIME[2008-12-12 08:55:08 +09:00]] 版) <http://permalink.gmane.org/gmane.org.w3c.whatwg.discuss/17107>
]REFS]

[2] [CITE@en[IEでのonBeforeUnload の挙動 | Inside ASCADE]] ([TIME[2009-02-20 05:46:35 +09:00]] 版) <http://inside.ascade.co.jp/node/58>

><a> タグの href で javascipt を呼び出した場合に location を変化させない(つもり)にもかかわらず、onBeforeunload イベントが発生したことです。ちなみにFirefoxではこの場合にイベントは発生しません。

>
- 多少仕様の記述と異なるが大体MSDNのドキュメント通り
- href="javascript:xx" でonBeforeunload が発生するのは少し困る
- document.close ではonBeforeunload が発生しないが別に困らない

[3] [CODE(DOMe)@en[[[beforeunload]]]] 内で [CODE(HTMLe)@en[[[img]]]] により [[fetch]] を行うと、
[[Chrome]] と [[IE10]] では実際にアクセスが発生しますが、 [[Firefox]] では発生しません。 [TIME[2013-07-04T03:05:58.500Z]]

[4] [CITE@en[Web Applications 1.0 r8282     OnBeforeUnloadEventHandler is supposed to allow null return values (and some markup error fixes, oops)]]
( ([TIME[2013-11-14 07:45:00 +09:00]] 版))
<http://html5.org/tools/web-apps-tracker?from=8281&to=8282>

[5] [CITE[IRC logs: freenode / #whatwg / 20150115]]
( ([TIME[2015-01-16 11:47:48 +09:00]] 版))
<http://krijnhoetmer.nl/irc-logs/whatwg/20150115#l-693>

[23] [CITE[Update on standardizing shadow DOM and custom elements — Anne’s Blog]]
([TIME[2015-07-29 12:37:46 +09:00]] 版)
<https://annevankesteren.nl/2015/07/shadow-dom-custom-elements-update>

[24] [CITE[IRC logs: freenode / #whatwg / 20150727]]
([TIME[2015-07-30 10:13:04 +09:00]] 版)
<http://krijnhoetmer.nl/irc-logs/whatwg/20150727>

[25] [CITE@en[Block modal dialogs by default in sandboxed documents · whatwg/html@bbccfc9]] ([TIME[2015-09-10 00:20:23 +09:00]] 版) <https://github.com/whatwg/html/commit/bbccfc976754def0c187ac8ce5891d2fb20dfc15>

[37] [CITE@en[Remove the storage mutex due to lack of implementation · whatwg/html@1b918cf]] ([TIME[2015-12-16 14:38:10 +09:00]] 版) <https://github.com/whatwg/html/commit/1b918cf72fcbba011f83b92ab5d1f483fb1cafa3>

[38] [CITE@en[Allow truncation of alert/confirm/prompt text · whatwg/html@56f5c5e]]
([TIME[2016-02-21 12:50:32 +09:00]] 版)
<https://github.com/whatwg/html/commit/56f5c5e39b8b43b4bd4a8007fdc56ad797a5b2a7>

[44] [CITE@en[Ignore beforeunload event return value · whatwg/html@01d9870]]
([TIME[2016-04-09 13:26:44 +09:00]] 版)
<https://github.com/whatwg/html/commit/01d9870a6873e6d6410c3abf9fe38533646631b9>

[39] [CITE@en[25002 – Event.returnValue]]
([TIME[2016-04-10 12:09:10 +09:00]] 版)
<https://www.w3.org/Bugs/Public/show_bug.cgi?id=25002>

[47] [CITE@en[Be more explicit that prompting before unloading is optional]]
( ([[domenic]]著, [TIME[2016-05-20 15:52:39 +09:00]]))
<https://github.com/whatwg/html/commit/8ef5d945d9c4298a0d66f81543f6a7e83634f39b>

[52] [CITE@en['''['''''']''' (0) Placeholder for beforeunload/unload; interaction of document.o…]]
( ([[Hixie]]著, [TIME[2007-04-21 14:30:09 +09:00]]))
<https://github.com/whatwg/html/commit/c1994a485446ab0fbfb7048df10bb081947f6405>

[FIG(quote)[
[FIGCAPTION[
[53] [CITE[API Deprecations and Removals in Chrome 51 | Web Updates - Google Developers]]
( ([TIME[2016-08-03 01:42:01 +09:00]]))
<https://developers.google.com/web/updates/2016/04/chrome-51-deprecations#remove-custom-messages-in-onbeforeload-dialogs>
]FIGCAPTION]

> A window’s onbeforeunload property may be set to a function that returns a string that is shown to the user in a dialog box to confirm that the user wants to navigate away. This was intended to prevent users from losing data during navigation. Unfortunately, it is often used to scam users.
> Starting in Chrome 51, a custom string will no longer be shown to the user. Chrome will still show a dialog to prevent users from losing data, but it’s contents will be set by the browser instead of the web page.
> With this change, Chrome will be consistent with Safari 9.1 and later, as well as Firefox 4 and later.

]FIG]


[59] [CITE@en[Fix onbeforeunload event handler processing]]
([[domenic]]著, [TIME[2017-02-15 05:15:01 +09:00]])
<https://github.com/whatwg/html/commit/fb7e621eab8a437df7ac524e547e31e117b7fce5>

[60] [CITE@en[Fix event handler processing algorithm special cases]]
([[domenic]]著, [TIME[2017-03-16 05:16:07 +09:00]])
<https://github.com/whatwg/html/commit/c065e991b65e10a1fc77ba77fed9f0822ff6858b>

[61] [CITE@en[Chromium policy on JavaScript dialogs  |  Web  |  Google Developers]]
( ([TIME[2017-03-31 00:28:36 +09:00]]))
<https://developers.google.com/web/updates/2017/03/dialogs-policy>

[77] [CITE@en[document.createEvent() supports lots of events that most UAs do not support initializers for · Issue #362 · whatwg/dom]]
([TIME[2017-08-08 21:48:03 +09:00]])
<https://github.com/whatwg/dom/issues/362>

[78] [CITE@en[document.open() simplifications, part 1]]
([[TimothyGu]]著, [TIME[2018-08-17 07:20:44 +09:00]])
<https://github.com/whatwg/html/commit/6f769b8089a843066aa19f5991405bf4c84458b3>

[79] [CITE@en[How should document.open handle the beforeunload handler tearing down the world? · Issue #3306 · whatwg/html]]
([TIME[2018-08-23 20:17:34 +09:00]])
<https://github.com/whatwg/html/issues/3306>

[80] [CITE@en[document.open() simplifications: realm creation, unloading, tasks removal by TimothyGu · Pull Request #3918 · whatwg/html]]
([TIME[2018-08-23 20:19:30 +09:00]])
<https://github.com/whatwg/html/pull/3918>