#はじめに
イベント伝搬の基礎知識をふまえたバブリングの挙動についてまとめます。
また、不慮のバブリングが発生した際に止めるプロパティについても言及していきます。
実務の中で、addEventListenerを多用するくせに正確なイベント伝搬の仕様を把握していなかったため、基礎ではありますがまとめました。
#イベント伝搬(プロパゲーション)
イベントバブリングとイベントキャプチャリングの両方をカバーする用語。
伝搬はウィンドウからイベントターゲットの方向とその逆の双方向で、伝搬は3つのフェーズに分かれる。
- ウィンドウからイベントターゲットの親まで:キャプチャーフェーズ
- イベントターゲット:ターゲットフェーズ
- イベントターゲットの親から戻ってウィンドウまで:バブルフェーズ
##キャプチャーフェーズ
通常では呼ばれず、addEventListenerの3番目のパラメータをtrueとして登録された場合にのみ呼ばれる。(capturerリスナー)。
パラメータを省略するとデフォルト値のfalseなので通常のバブルフェーズで呼ばれる。
DOMツリーをたどってルート要素から発生要素を探しに行く
##イベントターゲットフェーズ
イベントターゲットに登録されたすべてのリスナーが、captureフラグの値にかかわらず呼び出される。発生要素を検出する。
##イベントバブリングフェーズ
呼ばれるのは、capturer以外のリスナーになる。addEventListener()の3番目のパラメータがfalseで登録されているリスナー(初期値がfalseなので、あえて3番目のパラメータをtrueとして登録していないすべてのaddEventListener)。
要素上でイベントが起きると、最初にその上のハンドラが実行され、次にその親のハンドラが実行され、他の祖先に到達するまでそれらが行われる。
らしいのですがいまいち分からず。
下の図がとても理解しやすかったです。
#バブリングとは
イベントバブリングフェーズの伝達の過程のプロセスを “バブリング” と呼ぶ。水の中の泡のように、イベントが内部の要素から親に至るまで “バブル” しているためらしい。ルート要素まで遡る。上の図で言う緑線の工程。
See the Pen OJRWGyp by arichel (@arichel_unt) on CodePen.
上記のタグをクリックすると、親の要素のイベントへ伝達します。視覚的に重なっているものに伝達するのは感覚で理解していましたが、``````の部分であっても、親要素である```
- load
- unload
- mouseenter
- mouseleave
- focus
- blur
親要素が同種のイベント(今回はClickイベント)を持っていた場合、
キャプチャフェーズでイベントを発生させてしまうよう設定するのがuseCapture=true。
逆にuseCaptureをfalseまたは省略すると親要素のイベントはすべてバブリングフェーズで実行される。
#バブリングを止める
バブリングイベントはターゲット要素からまっすぐ上がっていき、 要素まで到達し、documentオブジェクトに移動し、イベントはwindowにも到達し、そのパス上のすべてのハンドラを呼び出す。
それらのイベントの処理が完了したと判断させ、バブリングを止めることもできる。
選択肢にのぼるのは以下の2つ。
stopPropagation() | 親要素への伝播(バブリング)をキャンセルする。 |
stopImmediatePropagation() | 同じイベントを受け取る他のリスナーの呼び出しを防ぐ。 |
stopPropagation() メソッドは現在のノードの処理が終わってから中断したい場合に使用する。
stopImmediatePropagation() メソッドは直ちに中断したい場合に使用する。
preventDefault() はその要素のイベント自体をキャンセルするがバブリングをキャンセルするものではなさそう。
##stopPropagation()を使ってみた
実務では主にモーダルのコンテンツ内のクリック時の制御に使用しました。
See the Pen ExgmaXr by arichel (@arichel_unt) on CodePen.
閉じるボタンの他に背景をクリックした際もモーダルを閉じることがほとんどなので、一番親要素の<div class="modal">
をクリックした際にモーダルを閉じるようイベントを作成。
しかし当然ママだと、モーダルのどこをクリックしても閉じてしまいます。
コンテンツ要素の<div class="modal_content">
をクリックした際はstopPropagation()
とすることで、望んだ挙動を実現できました。
以前は<div class="modal_bg">
のようなものを背景に配置してイベントを持たせていましたが、どちらがいいか検証中です。。。
stopPropagation()
を仕込むと、<div class="modal_content">
内でイベントを発生させた際も(モーダル内のボタン等)すべて<div class="modal_content">
で止めてしまうため、不要に伝達を止めないよう留意しておく必要はありそうです。
#参考
http://site.m-bsys.com/knowledge/capturing_and_bubbling
https://ja.javascript.info/bubbling-and-capturing