DOMDOMタイムス#14: Eventインタフェースのbubblingとcomposed
めちゃくちゃ久しぶりの更新になってしまったドムドムタイムスです👶
(JSConfに全身全霊を捧げていました!)
さて、今日はEvent
インターフェースのbubbling
とcomposed
についてチラチラ見ていってみます。
イベントのインターフェースのおさらい
まずイベントがどういうインターフェースになっていたか軽く確認してみましょう!
例えばclick
イベントはPointerEvent
インターフェースを持ったオブジェクトの1つでした。
このPointerEvent
インターフェースとやらは辿っていってみると、下のようにEvent
インターフェースをinheritしているわけですね。
Event <- UIEvent <- MouseEvent <- PointerEvent
Event
インターフェースを起点として、いろいろなイベントが実装されています。
例えばEvent
インターフェースをinheritしているUIEventと同じレベルにSubmitEvent
やCustomEvent
などがあったり、マニアックなものだとGamepadEvent
なんかもあったり。もちろんもっとたくさんあります。
また、UIEvent
をinheritしているものとなると、その名の通りでUIに関わるイベントがてんこもりです🌞
MouseEvent
からKeyboardEvent
、InputEvent
、TouchEvent
なんかも。
気になる人はMDNのこのページからディグってみよう!
Event
インターフェースのbubbling
とcomposed
Event
インターフェースが持つbubbling
とcomposed
なる2つのプロパティが今日のテーマです👀
これらプロパティはそれぞれ下記を定義していて、その値は各イベントごとに決められているわけヨネ。もちろんCustomEvent
なんかは自分で決められますが👶
-
bubbling
: trueならバブリングする。falseならしない -
composed
: trueならイベントはバブリング/キャプチャリングのときにshadow rootを超える。falseならshadow root止まり。
イベントの代表例をいくつかマッピングしてみると、以下のようになります。
bubbling: true | bubbling: false | |
---|---|---|
composed: true | Aグループ(例: click(*1)) | Bグループ(例: focus, blur) |
composed: false | Cグループ(例: mutationEvent(*2)) | Dグループ(例: mouseenter, mouseleave) |
*1: なんだかんだほとんどのUI Eventがここ
*2: documentが絡むもの(e.g.DOMNodeInsertedIntoDocument
)は当てはまらない
で、ちょっと分かりやすくなるようにイベントの流れを図にしてみるとこんな感じ(ただしevent targetはshadow root外からだとshadow rootを持つ要素に書き換えられちゃうことに注意!!)。
こうだろ〜!と思ってると意外と違う、なんてことがDOMを触っていると(自分は)あります🐤
変な動きしてるなと思ったときは、このあたりを疑ってみるのもいいかもしれません。
意外だったイベント
自分が初めて見たとき、なんで!?ってなったものを2つ書いておいてみます。
scroll
イベント
1つめはscroll
イベントがバブリングしないという有名な話です🌞
自分は最初これを知らずにめちゃくちゃハマりました。
というか、そもそもscroll
のbubbling
やcomposed
はどこに仕様が載っているのでしょうか……?分かる方いたら教えてください😭
ちなみにChrome(v119)でやってみたところ、target要素でだけはバブリングフェーズでも反応しているっぽいですね。Firefox(v119)ではそんなことなさそうですが……汗
Chrome(v119) : window(capturing) -> target(capturing) -> target(bubbling) -> おわり
Firefox(v119): window(capturing) -> target(capturing) -> おわり
一方で、少なくともChrome(v119)やFirefox(v119)においては、少なくともdocument
がターゲットなら、scroll
イベントのbubbling
プロパティはtrue
です。バブリングします。
でも、window
オブジェクトのすぐ下なので「バブリングしていると言ってもねえ……」という感が自分にはありますが👶
とりあえず色々と「なんで!?」感あるので、ここにノミネートされています。とりあえず仕様を見つけたいところです。
input
イベントとchange
イベント
input
イベントとchange
イベントも若干クセがあります。
両者ともに同じようなイベントに見えますがcomposed
の値が違うわけですね。input
がtrue
、change
がfalse
です。
ちなみにこれはwhatwgでも議論になっています。歴史的背景、という感じなので興味がある人は読んでみて下さい!
まとめ
イベントごとに異なるbubbling
とcomposed
はたまにハマりどころになる印象があります。
前にFloating UI(旧Popper)のデバッグをしているときかなんかに、それこそscroll
のバブリングでハマった覚えがあります。
こりゃ大変ですわ……😭
Discussion