1. ホーム
  2. javascript

[解決済み] すべてのjqueryイベントは$(document)にバインドされるべきですか?

2022-05-02 10:57:19

質問

これはどこから来ているのか

私がjQueryを覚えたての頃は、普通にこんな感じでイベントを付けてました。

$('.my-widget a').click(function() {
    $(this).toggleClass('active');
});

セレクタの速度とイベントデリゲーションについて学んだ後、いくつかの場所で "jQuery event delegation will make your code faster." と書いてあったので、このようなコードを書くようになったのです。

$('.my-widget').on('click','a',function() {
    $(this).toggleClass('active');
});

これは、非推奨の .live() イベントの動作を再現する方法としても推奨されていました。私のサイトの多くは常にウィジェットを動的に追加/削除しているので、これは私にとっては重要なことです。というのも、すでに存在するコンテナ '.my-widget' に追加された要素のみがこの挙動を得るからです。このコードが実行された後に別の HTML ブロックを動的に追加した場合、それらの要素はそれらにバインドされたイベントを取得しません。このように。

setTimeout(function() {
    $('body').append('<div class="my-widget"><a>Click does nothing</a></div>');
}, 1000);



実現したいこと

  1. .live() の古い動作 // まだ存在しない要素にイベントをアタッチすることを意味します。
  2. .on()のメリット
  3. イベントをバインドするための最速のパフォーマンス
  4. シンプルなイベント管理方法

今は、すべてのイベントをこのように添付しています。

$(document).on('click.my-widget-namespace', '.my-widget a', function() {
    $(this).toggleClass('active');
});

これは、私の目標をすべて満たしているようです。(確かにIEではなぜか遅い。理由は不明?) イベントが1つの要素にのみ関連付けられ、セカンダリセレクタはイベントが発生したときにのみ評価されるため、高速です(ここで間違っていたら訂正してください)。名前空間は、イベントリスナーを簡単に切り替えることができるので、素晴らしいです。



私の解決策/質問

そこで、jQueryのイベントは常に$(document)にバインドされるべきだと思い始めています。

そうしたくない理由でもあるのでしょうか?

これはベストプラクティスと言えるでしょうか?そうでない場合、その理由は?

全部読んでくれた方、ありがとうございます。ご意見・ご感想がありましたら、よろしくお願いします。

想定していること

  1. をサポートするjQueryを使用します。 .on() // バージョン1.7以上
  2. 動的に追加されるコンテンツにイベントを追加したい場合

読み物/例

  1. http://24ways.org/2011/your-jquery-now-with-less-suck
  2. http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
  3. http://www.jasonbuckboyer.com/playground/speed/speed.html
  4. http://api.jquery.com/on/

解決方法は?

いいえ - すべての委譲されたイベントハンドラを document オブジェクトを作成します。 これはおそらく、作成できる中で最もパフォーマンスの悪いシナリオでしょう。

まず第一に、イベントデリゲーションは必ずしもコードを速くするものではありません。 有利な場合もあれば、そうでない場合もあります。 イベントデレゲーションは、実際にイベントデレゲーションが必要なとき、そしてその恩恵を受けるときに使うべきものです。 そうでない場合は、イベントが発生したオブジェクトに直接イベントハンドラをバインドした方が、一般的に効率的です。

次に、委任されたすべてのイベントをドキュメントレベルでバインドしてはいけません。 これはまさに .live() が非推奨になったのは、この方法でたくさんのイベントを束ねた場合、非常に非効率的だからです。 なぜなら、たくさんのイベントをこの方法でバインドするのは非常に効率が悪いからです。

第三に、すべてのイベントがうまくいくわけではなく、すべての問題がデリゲーションで解決できるわけでもありません。 たとえば、入力コントロールのキーイベントをインターセプトして、無効なキーが入力コントロールに入力されるのをブロックしたい場合、デリゲートされたイベントハンドラまでイベントがバブルアップするまでに、すでに入力コントロールによって処理されているので、その動作に影響を与えるには遅すぎるのです。

ここでは、イベントの委譲が必要な場合、または有利な場合を説明します。

  • イベントを捕捉しているオブジェクトが動的に作成/削除され、新しいオブジェクトを作成するたびに明示的にイベントハンドラを再バインドすることなく、それらのオブジェクトのイベントを捕捉したい場合。
  • 全く同じイベントハンドラを必要とするオブジェクトがたくさんある場合(lotsは少なくとも数百)。 この場合、何百もの直接のイベントハンドラをバインドするよりも、ひとつのデリゲートイベントハンドラをバインドしたほうが、セットアップ時に効率的である場合があります。 ただし、デリゲートイベントハンドラは、ダイレクトイベントハンドラに比べ、常に実行時の効率は悪くなります。
  • ドキュメント内の任意の要素で発生したイベントを (ドキュメント内のより高いレベルで) 捕捉しようとする場合。
  • ページ内の問題や機能を解決するために、イベントバブリングと stopPropagation() を明示的に使用する設計になっている場合。

これをもう少し理解するためには、jQueryのデリゲートイベントハンドラがどのように動作するかを理解する必要があります。 このようなものを呼び出すと

$("#myParent").on('click', 'button.actionButton', myFn);

これは、汎用の jQuery イベントハンドラを #myParent オブジェクトを作成します。 クリックイベントがこのデリゲートイベントハンドラにバブルアップされると、jQueryはこのオブジェクトに接続されているデリゲートイベントハンドラのリストを調べ、イベントの発生要素がデリゲートイベントハンドラのセレクタのいずれかにマッチするかどうかを確認する必要があります。

セレクタはかなり複雑なため、jQueryは各セレクタを解析し、元のイベントターゲットの特性と比較して、各セレクタと一致するかどうかを確認する必要があることを意味します。 これは決して安い操作ではありません。 1つだけなら大したことはありませんが、すべてのセレクタをドキュメントオブジェクトに配置し、バブル化したイベントごとに比較するセレクタが何百もあった場合、イベント処理のパフォーマンスに深刻な支障をきたし始める可能性があります。

このため、委譲されたイベントハンドラは、できるだけターゲットオブジェクトに近い場所に設置するようにしたいものです。 そうすれば、各デリゲートイベントハンドラを経由するイベントの数が少なくなり、パフォーマンスが向上します。 すべてのデリゲートイベントをドキュメントオブジェクトに配置すると、バブル化したすべてのイベントがすべてのデリゲートイベントハンドラを経由して、可能な限りのデリゲートイベントセレクタに対して評価されなければならないため、最悪のパフォーマンスとなります。 これはまさに .live() が非推奨なのは、このような .live() は、非常に非効率的であることが判明しました。


そこで、最適化されたパフォーマンスを実現するために

  1. 委譲されたイベント処理は、実際に必要な機能を提供する場合、またはパフォーマンスを向上させる場合にのみ使用してください。 実際に必要でないときに、簡単だからという理由で常に使用するのはやめましょう。 イベントディスパッチ時のパフォーマンスは、直接のイベントバインディングよりも悪くなります。
  2. デリゲートイベントハンドラは、できるだけイベントの発生源に近い親にアタッチすること。 もし動的な要素のイベントを捕捉するために委譲されたイベントハンドリングを使用するのであれば、 それ自身が動的ではない最も近い親を選択します。
  3. 委譲されたイベントハンドラには、評価しやすいセレクタを使用する。 そのため、できるだけ効率的なセレクタを選択するか、オブジェクトに単純なクラスを追加して、より単純なセレクタを使用できるようにすると、委譲されたイベントハンドラのパフォーマンスが向上します。