4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pointerenterはバブリングしないらしい

Last updated at Posted at 2024-12-10

はじめに

タイトルにあるとおり、DOMで発生するイベントの1つである pointerenter イベントはバブリングしないというお話です。

去年のアドベントカレンダーで書いたDOMイベントの伝搬に関する記事の延長です。バブリングってなに?という方はまずこちらを読んでいただければざっくり理解できるかと思います。

「バブリングしない」とは?

Mozillaのドキュメントより、Pointer Event の pointerenter の説明に

このイベントは、ポインティングデバイスが要素またはその子孫の 1 つのヒットテスト境界内に移動したときに発生します。 これには、ホバーに対応していない機器からの pointerdown イベントの結果も含まれます(pointerdown を参照)。 この種類のイベントは pointerover に似ていますが、バブリングしないという点が異なります。

とあります。

ここでいう「バブリングしない」というのがどういうことか、後述の章でいろいろ考察していますが、先に結論を書くと

子要素から親要素への伝搬がなく、また親要素のイベント伝搬から順に実行される

ということのようです。

・・・とだけいってもよくわからないと思うので、以降の章でパブリングの有無がイベント伝搬にどう影響するのか調べた内容を紹介します。

バブリングする/しないイベント伝搬の動き

百聞は一見に如かず。ということでサンプルを用意しました。

pointerover

まずはじめに、バブリングするイベントである pointerover のサンプルです。

See the Pen check-pointerenter by www-tacos (@www-tacos) on CodePen.

赤色が親で青色が子になっていて、親と子それぞれにCapturingとBubblingそれぞれで発火するイベントリスナーを設定しています。

青色の四角形にマウスカーソルを重ねる(スマホならタッチする)と
親のCapturing → 子のCapturing → 子のBubbling → 親のBubbling
の順にイベントが検知されると思います。

こちらはキャプチャリング・バブリングの想定通りの動きですね。

pointerenter①

つづいて、以下は pointerenter のサンプル1つめです。

See the Pen pointerenter-has-no-bubbling-1 by www-tacos (@www-tacos) on CodePen.

ここではまず親と子の両方に対して addEventLister の第3引数を指定せずにイベントリスナーを設定しました。

さきほどと同様に青色の四角形にマウスカーソルを重ねる(スマホならタッチする)と
親のBubbling → 子のBubbling
の順にイベントが検知されると思います。

つまり第3引数の指定なしで設定したイベントリスナーがキャプチャリングフェーズで発火するのと同じように動いていることがわかります。

といったところで、まずこの結果からバブリングしないイベントは内部的にキャプチャリングフェーズに置き換えているのかな?と予想しました。まあ最終的にこの予想は外れるのですが...

pointerenter②

さいごに、以下は pointerenter のサンプル2つめです。

See the Pen pointerenter-has-no-bubbling by www-tacos (@www-tacos) on CodePen.

ここでは addEventLister の第3引数を指定してのイベントリスナー設定も追加で行っています。

さきほどと同様に青色の四角形にマウスカーソルを重ねる(スマホならタッチする)と
親のCapturing → 親のBubbling → 親のCapturing → 子のCapturing → 子のBubbling
の順にイベントが検知されると思います。


ん?親のCapturingが2回もでている?どういうことなんだ...

「バブリングしない」の実態

よくわからないのでいくつか追加で調べてみました。

  • 2つめのサンプルでは event.target は以下のようになっていた。

    • 1.親のCapturing (target=div#parent)
    • 2.親のBubbling (target=div#parent)
    • 3.親のCapturing (target=div#child)
    • 4.子のCapturing (target=div#child)
    • 5.子のBubbling (target=div#child)
  • pointeroverの時は event.target はすべてdiv#childだった。

上記の結果から、おそらく以下の図のように親のイベント発火→子のイベント発火の順に実行しているのだと思います。

図.バブリングしないイベントの発火順序

こういうロジックだからこそ addEventListener の第3引数で明示的にキャプチャリングフェーズを指定してなくても親のイベントが先に実行され、結果的にキャプチャリングフェーズで実行しているように見えるのだと思われます。

したがって、最初にも書いたように、バブリングしないというのは

子要素から親要素への伝搬がなく、また親要素のイベント伝搬から順に実行される

ということなのだと思います。

ちなみにこれらはあくまで結果からの推測です。もし間違っていたらごめんなさい。

バブリングしないイベント

pointerenter 以外にもバブリングしないイベントがいくつかあるようです。

  • pointerleave (類似イベントの pointerout はバブリングする)
  • mouseenter
  • mouseleave
  • focus
  • blur
  • scroll
  • resize

バブリングしない理由をChatGPTに聞いたところ「用途が特定の要素に限定されるため、親要素への伝播が不要」だからだそうです。

たしかにfocusなんかは子要素から親要素に伝搬してしまうと最終的に親要素にフォーカスしたようになってしまうので伝搬は不要そうですね。

ただ親要素のイベントリスナーが無効になるわけではないので、親子要素にこれらのイベントリスナーを設定している場合は以前考慮が必要になりそうです。

おわりに

いかがでしかた?
当記事が(存在するかわからないですが)バブリングに悩む方の助けになれば幸いです。

最後まで読んでいただきありがとうございました。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?