[更新あり] Chrome 120で dialog要素の onCancel イベントがトリガーしない
-
2023-12-06: CloseWatcher API を含む Chrome 120 リリース、同時に
onCancel
がトリガーしない問題発生 - 2024-01-08: CloseWatcher API が無効化されたことで一時的に問題が解消
-
2024-06-12: Chrome 126 リリース、
onCancel
問題が対処された CloseWatcher API が再度有効化される予定
Chrome 120 で新たに追加された Close Watcher API によって、<dialog>
要素にアタッチされた onCancel
イベントがトリガーしなくなる模様。
this is an intentional change as part of https://chromestatus.com/feature/4722261258928128, to prevent excessive events firing when the user does not interact with the <dialog>. It is now specified and required by the HTML Standard.
— https://bugs.chromium.org/p/chromium/issues/detail?id=1510389#c8
<dialog>
で過剰にイベントを発火しないようにと、HTML の仕様で決められたとのこと。
Demo
Esc でモーダルを閉じると、コンソールに、onCancel
イベントのログが出力される。Chrome 120 で見ると、ログが出ていないことがわかる。
何らかの理由で、Esc キーでダイアログを閉じないようにしたい場合、以下の実装は Chrome 120 以降では動作しないことになる。
// Chrome 120 以降では動作しない
dialog.addEventListener('cancel', (e) => {
e.preventDefault();
})
Chrome 120 でも cancel
イベントが呼ばれる時もある模様。一貫してないのがちょっとややこしい。
例: モーダル表示後、背景の透過部分 (::backdrop
) をクリックした後に Esc キーを押すと、 onCancel
が呼ばれる。e.preventDefault()
をしていれば、閉じる処理も抑制される。
CloseWatcher で Esc を抑制する (close イベントを使うパターン)
Esc の他、Android の戻るジェスチャーも抑制可能になる。
<button type="button" id="open">
Open modal
</button>
<dialog id="modal">
<p>
Esc key is disabled!
</p>
<button type="button" id="close">
Close modal
</button>
</dialog>
const open = document.querySelector('#open');
const close = document.querySelector('#close');
const dialog = document.querySelector('#modal');
open.addEventListener('click', () => {
dialog.showModal();
// Esc キーを抑制 (Chrome 120 以降)
if ('CloseWatcher' in window) {
let closeWatcher;
const initCloseWatcher = () => {
closeWatcher = new CloseWatcher();
// CloseWatcher は消費すると2回目はトリガーしないので、消費の度に新しい CloseWatcher を作る
closeWatcher.addEventListener('close', initCloseWatcher);
}
initCloseWatcher();
// ダイアログが実際に閉じる際に既存の CloseWatcher を破棄
dialog.addEventListener('close', () => closeWatcher?.destroy(), { once: true });
}
});
close.addEventListener('click', () => dialog.close());
🚨更新: 2024-01-08
破壊的な変更が問題視され、いったんリモートで CloseWatcher API が無効にされた。
Chrome 120 でも、元の挙動に戻っているはず。