「UIのボタンを押した」だとかを実装するのに思いつくのはまずonClickです。しかしonClickではなくaddEventListenerを使うと、押したときのカーソル位置だとかも取得できて便利です。addEventListenerについて詳しくはこちらのサイトの『イベントとは』以降でbry-ful様が詳しく説明なさってくれています。
しかし、JavaScriptのプロパティなどが載っているサイトでさらに詳しく調べてみると「イベントのバブリングがどうたらで、親にも伝播し~」など、(知らない人にとっては)ちょっとわけわからんことが書いてあります。
どうやらイベントリスナーを付けたパーツ以外にもイベントは起こるようですが、その仕組みをちょっとサイトの説明を解釈しつつ動画にしてみました。
いかがでしょうか?「イベント伝播」の説明がわかりにくいのには2つ原因があると思います。
1つは「パーツをフォルダ階層風に見ると親が上だが、レイヤー風に見ると親が下なので、説明を呼んでるうちにイメージがわけわからなくなる」ことですね。レイヤー風配置を裏返して横から見るイメージをするとわかりやすいと思います。これだとバブリングという言葉から連想する「泡が上に上がってる感」も保ちつつイメージできます。
2つ目は「『イベントが起こる』という言葉が、日常の意味とちょっと違う」ところです。
日常の意味で考えると、『イベントが起こる』とは「イベントリスナー『addEventListener(“イベント名”,関数)』の関数の部分が実行されることだ」と思ってしまいますが、そうではないようです。動画の通り、『イベントが起こる』とは(例えばマウスダウンなら)ボタンが押されたときに、その下の重なってる要素群も含めて「あなたがた、押されましたよ。んでついでに押されたとこの情報(イベントオブジェクト、e)はこれですよ」と気づかせることのようです。
そして次に起こる「各パーツがそれぞれリスナーをもってたら、それの関数が発動する。その関数は押されたところの情報(イベントオブジェクト、e)を使える」というのはイベントではないのです。
バブリングフェーズでイベント伝播することを「バブリング」と呼ぶみたいですが、
addEventListener(“イベント名”,関数,true)
とtrueにした場合、バブリングフェーズじゃなくキャプチャリングフェーズでイベントが起こりますが、これも広義のバブリングと呼ぶのかはわかりません。どうなんだろ…?
よくある間違い危惧に「子パーツにイベントリスナーを付けたが、親パーツの数だけそのイベントリスナーの関数が発動しちゃうんじゃないか」ってのがあります。そんなことはありません。むしろそうしたいときは、各親パーツにイベントリスナーを付け、同じ関数を引数にすればよいです。
また逆に、起こり得る想定外の事態は「親パーツにだけイベントリスナーを付けたが、子パーツを押しても関数発動するんだけども?」って事態です。これは動画の通り、バブリングによって「子パーツが押されたら親パーツも押されたことになる」からです。下記のe.targetやe.stopPropagation()で対処できます。
補足
動画の通り、バブリングの仕組みを使ってさまざまなことができます。
*以下は「イベントがマウスダウンだとして」の説明です。別のイベントについては適宜「押されたら」を「キー入力したら」などに置き換えてお読みください。
e.targetは押されたパーツ自身、e.currentTargetは『今処理中の、伝播して伝わったパーツ』なので、
例えば「もしもe.targetとe.currentTargetが違えば何もしない」などで、押されたパーツ以外には何もしないことができます。
他にもe.stopPropagation()で伝播を止めたり、パーツ.dispatchEvent()でイベントが実際は起こってないのに起こしたりできます。dispatchって単語、「patchのdisだから『修正しない』とかいう意味」と思ってましたが「急送する」って意味なんですね。全然違った…イベントをパーツに急送しますよ!ってことですね。
またふと「2組の、親子位置が交差してるパーツ構成だとどうなるのか?」と考えて実験してみました。しかし位置指定でそのようにパーツを組んでも、親パーツの大きさ以上の位置に子パーツを置くと見えなくなるので、そのような構成自体が不可能でした。このことから、(裏返さないレイヤー風に見て)子パーツを押すと、下の重なってるパーツも全てそれぞれでイベントが起こると解釈して問題ないでしょう。
AE特有の問題
そしてなぜかAEでは上記の通りにならないことがあります。バグなのか、解釈間違いがあるのか…
■一番親のwindowにはイベントリスナーを付けても何も起こらない。
■addEventListener(“イベント名”,関数,true)としてキャプチャリングフェーズでイベントが起こるようにすると、ターゲットフェーズ(パーツ自身)でイベントが起こらない。
■キーイベントはエディットテキストなどがアクティブ(入力欄に文字入力できる状態)でないと起こらない。これはAE特有なのかは未知。キーイベントをパネルなどで起こしたい場合はエディットテキストを作ってアクティブにしといてhideしておく、という処理が必要。
以上です!
知っておくと案外面白いことができたり、問題が解決したりするのでオススメです!
参考サイト:
頭脳一式 【JavaScript入門】イベントのバブリングとキャプチャリング
WPJ ちゃんと知ってる?JavaScriptのイベントバブリングを学ぼう