はじめに
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
は発火させることはできないのだろうか。私の場合は、それがやりたかった為に無意識にコードをこじらせている部分があります。。。