なんでもかんでも document にリスナを付けるべきじゃない
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ 全部キャプチャをリスナごとにやってるとムダが多すぎ
◆ イベントバブリングの機能を捨ててる
◆ devtools でリスナ探すときに苦労する
◆ イベントバブリングの機能を捨ててる
◆ devtools でリスナ探すときに苦労する
jQuery を使ってる人に多い印象ですが body や document や window と 全体のイベントを受け取れるところに on でリスナをつけてるのをけっこうみます
みたいなものです
動的に画面が変わる場合は直接ボタンにつけるより常に存在する親要素に付けておいたほうが楽です
ですがデメリットも多いです
遅く感じないからと言っても必要ない処理はないほうがいいものです
検索結果を表示するテーブルで 各行の ×ボタンにリスナをつけたいとします
行は入れ替わるので親要素につける場合は table につければ十分です
これを document につけると テーブルの外の p タグだったりページのヘッダ―だったり他のボタンを押したときも全部処理が実行されています
その中で対象にしたターゲットから発生したイベントじゃないので何もしていないだけです
ポップアップをだしたりダイアログを出したり画像をスライドショーで見せたり 色々な機能を詰め込んでいるページでそれをやるとリスナがかなりたくさんつきます
何十何百とついてるページもあります
必要な箇所だけならともかくどこでイベントを起こしてもそれだけ処理されてるなんて無駄すぎで気持ちのいいものではないです

これは Youtube の例です
Window とか document とかがリスナを設定した要素です
Youtube だと WebComponent が使われてるので ytd-app とかになっていますがこれは div みたいな要素です
あるリンクをクリックしたときの処理がどうなってるのか調べたい時 devtools で簡単に調べられます
JavaScript で数千行になっていてソースを読んでリスナを探すのが大変なときに便利です
でもリスナが全部 document についていたとすると関係ないところのリスナも全部出てしまいます
親要素にリスナをつける場合でも li が変わって ul は固定なら li 内ボタンのリスナは ul についてるものと想像できるので簡単に探せます
でも document なら全部 document なので結局全部見ていくことになります
必要以上に上の階層につけない方いいです
table>tbody>tr>td という DOM 構造なら td → tr → tbody → table です
同じ要素ならつけた順に実行されますが違う要素につけることであとから付けたものを先に実行させることもできます
また イベントは stopPropagation メソッドを呼び出すことでそこより上の要素にイベントを伝えないことも出来ます
tr で stopPropagation を実行すれば tbody や table につけたリスナは呼び出されません
これをうまく使えば便利なのですが 全部 document に付いていた場合はこういった使い方ができません
後から別の人がちゃんと ul などにリスナをつけたとしても そこで stopPropagation すると document についてるせいで他のリスナが動かなくなったり あとで付けたのに先に実行されてしまったりで 結局仕方なく document につけざるを得なくなって負の連鎖です
stopImmediatePropagation なら同じ要素のリスナもキャンセルできますが これだとリスナをつけた順の影響を受けます
何も考えずサンプルがそうだったからと document にリスナをつけてる人はこの仕組み自体を知らないから困ってすらないかもしれません
でも将来の自分や こういった仕組みを知ってる別の人が苦労するので適切なところにつけるようにしましょう
$(document).on("click", ".submit-btn", function(){
})
})
みたいなものです
動的に画面が変わる場合は直接ボタンにつけるより常に存在する親要素に付けておいたほうが楽です
ですがデメリットも多いです
- ムダな処理が増える
- devtools で探しづらい
- イベントバブリングの利点を捨ててる
ムダな処理
まずは単純にムダな処理が増えます遅く感じないからと言っても必要ない処理はないほうがいいものです
検索結果を表示するテーブルで 各行の ×ボタンにリスナをつけたいとします
行は入れ替わるので親要素につける場合は table につければ十分です
これを document につけると テーブルの外の p タグだったりページのヘッダ―だったり他のボタンを押したときも全部処理が実行されています
その中で対象にしたターゲットから発生したイベントじゃないので何もしていないだけです
ポップアップをだしたりダイアログを出したり画像をスライドショーで見せたり 色々な機能を詰め込んでいるページでそれをやるとリスナがかなりたくさんつきます
何十何百とついてるページもあります
必要な箇所だけならともかくどこでイベントを起こしてもそれだけ処理されてるなんて無駄すぎで気持ちのいいものではないです
devtools の一覧
devtools では選択した要素でイベントが起きたときに呼び出されるリスナの覧が見えます
これは Youtube の例です
Window とか document とかがリスナを設定した要素です
Youtube だと WebComponent が使われてるので ytd-app とかになっていますがこれは div みたいな要素です
あるリンクをクリックしたときの処理がどうなってるのか調べたい時 devtools で簡単に調べられます
JavaScript で数千行になっていてソースを読んでリスナを探すのが大変なときに便利です
でもリスナが全部 document についていたとすると関係ないところのリスナも全部出てしまいます
親要素にリスナをつける場合でも li が変わって ul は固定なら li 内ボタンのリスナは ul についてるものと想像できるので簡単に探せます
でも document なら全部 document なので結局全部見ていくことになります
必要以上に上の階層につけない方いいです
イベントバブリング
イベントは子要素から親要素に順番に伝わりますtable>tbody>tr>td という DOM 構造なら td → tr → tbody → table です
同じ要素ならつけた順に実行されますが違う要素につけることであとから付けたものを先に実行させることもできます
また イベントは stopPropagation メソッドを呼び出すことでそこより上の要素にイベントを伝えないことも出来ます
tr で stopPropagation を実行すれば tbody や table につけたリスナは呼び出されません
これをうまく使えば便利なのですが 全部 document に付いていた場合はこういった使い方ができません
後から別の人がちゃんと ul などにリスナをつけたとしても そこで stopPropagation すると document についてるせいで他のリスナが動かなくなったり あとで付けたのに先に実行されてしまったりで 結局仕方なく document につけざるを得なくなって負の連鎖です
stopImmediatePropagation なら同じ要素のリスナもキャンセルできますが これだとリスナをつけた順の影響を受けます
何も考えずサンプルがそうだったからと document にリスナをつけてる人はこの仕組み自体を知らないから困ってすらないかもしれません
でも将来の自分や こういった仕組みを知ってる別の人が苦労するので適切なところにつけるようにしましょう