はじめに
Javascriptでアプリ開発していると、なんとなく"予防的"に、preventDefaultとstopImmediatePropagationをコード中で併記する変な癖がついてしまっていました。(違いは理解しているつもりだったのですが、IME関連の処理を書いている時からpreventDefaultが効かなくなったりして混乱してしまったみたい。)
確認用として、これらでキャンセルできるイベントについて整理します。
テスト条件
次のようなHTMLで、contentEditableな要素を含むアプリを想定します。
<html>
<body>
<div id="div0" contentEditable="true">入力欄</div>
</body>
</html>
このHTMLに対して、Javascriptでは次のイベントリスナーを登録しておきます。
const div0 = document.getElementById("div0");
div0.addEventListener("keydown", OnKeydown1, false);
div0.addEventListener("keydown", OnKeydown2, false);
div0.addEventListener("input", OnInput, false);
div0.addEventListener("keyup", OnKeyup, false);
window.addEventListener("keydown", OnKeydownWin, false);
window.addEventListener("keyup", OnKeyupWin, false);
addEventListenerの登録順序も表記の順とします。
さて、これを実行し、要素div0の中でキー入力を行うと、次の順でイベントが発火します。
- OnKeydown1
- OnKeydown2
- OnKeydownWin
- OnInput
- OnKeyup
- OnKeyupWin
これがデフォルトの状況となります。OnKeydownWinとOnKeyupWinはバブリングにあたります。
発火を抑制できるイベント
上記の条件下において、OnKeydown1 イベントの中でpreventDefault、stopPropagation、stopImmediatePropagationのいずれかをcallした場合、2~6のイベントでキャンセルされるものを以下の表記に記載します。×印のついたイベントはキャンセルされ、なにも記述の場合は発火することを意味します。
| イベント名 | preventDefault | stopPropagation | stopImmediatePropagation |
|---|---|---|---|
| OnKeydown2 | × | ||
| OnKeydownWin | × | × | |
| OnInput | × | ||
| OnKeyup | |||
| OnKeyupWin |
preventDefaultとstopPropagationもしくはstopImmediatePropagationを併記した場合は、どちらかで×印が付いているイベントはキャンセルされます。
簡単に挙動を纏めると、次のようになります。preventDefaultでキャンセルできるのはデフォルト処理であり、今回のcontentEditableな要素に対してのキー入力では、文字入力がキャンセルされます。よってOnInputがキャンセルされます。 一方で、もう一つ登録されたOnkeydown2やバブリングであるOnkeydownWinはキャンセルされずに発火します。また、OnKeyupはkeydownとは独立したイベントとみなされ、キャンセルできません。
stopPropagationはバブリングだけをキャンセルします。よって、OnkeydownWinがキャンセルされます。しかし、 Onkeydown2は同じ要素の同じイベントに登録されたリスナーであってバブリングではないのでキャンセルできません。
Onkeydown2もキャンセルしたい場合にはstopImmediatePropagationを使います。これはバブリングに加えて、同じ要素の同じイベントに続けて登録されたリスナーもキャンセルします。
例外
IME制御は上記が当てはまらない場合があります。詳しくは以前のまとめへ
さいごに
まとめて思うのは、OnKeydown2だけをキャンセルして、バブルリングのOnKeydownWinは発火させることはできないのだろうか。私の場合は、それがやりたかった為に無意識にコードをこじらせている部分があります。。。