168

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

jQuery を初めて学んだとき、通常は次のようなイベントを添付しました。

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

セレクターの速度とイベントの委譲について詳しく学んだ後、「jQuery イベントの委譲によりコードが高速化される」という記述をいくつか読みました。そこで、次のようなコードを書き始めました。

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

これは、非推奨の .live() イベントの動作を複製するための推奨される方法でもありました。私のサイトの多くは常に動的にウィジェットを追加/削除するので、これは私にとって重要です。ただし、既存のコンテナ「.my-widget」に追加された要素のみが動作するため、上記は .live() とまったく同じようには動作しません。そのコードが実行された後に 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の方が遅いです。理由はわかりませんか?) 単一のイベントのみが単一の要素に関連付けられており、セカンダリセレクターはイベントが発生したときにのみ評価されるため、高速です(ここで間違っている場合は修正してください)。名前空間は、イベント リスナーを簡単に切り替えることができるため、すばらしい機能です。

私の解決策/質問

そのため、jQuery イベントは常に $(document) にバインドする必要があると考え始めています。
これをしたくない理由はありますか?
これはベストプラクティスと見なすことができますか? そうでない場合、なぜですか?

このすべてを読んだ場合は、ありがとうございます。あらゆる/すべてのフィードバック/洞察に感謝します。

仮定:

  1. .on()// 少なくともバージョン 1.7をサポートする jQuery を使用する
  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/
4

4 に答える 4

220

いいえ - すべての委任されたイベント ハンドラーをdocumentオブジェクトにバインドしないでください。これはおそらく、作成できる最悪のシナリオです。

まず、イベント委譲によって常にコードが高速化されるわけではありません。有利な場合もあれば、不利な場合もあります。実際にイベントの委任が必要な場合、およびそれによってメリットがある場合は、イベントの委任を使用する必要があります。それ以外の場合は、イベントが発生するオブジェクトにイベント ハンドラーを直接バインドする必要があります。これにより、通常はより効率的になります。

次に、委任されたすべてのイベントをドキュメント レベルでバインドしないでください。.live()多くのイベントがこのようにバインドされている場合、これは非常に非効率的であるため、これがまさに廃止された理由です。委任されたイベント処理の場合、それらを動的ではない最も近い親にバインドする方がはるかに効率的です。

第三に、すべてのイベントが機能するわけではなく、すべての問題が委任によって解決できるわけではありません。たとえば、入力コントロールのキー イベントをインターセプトし、無効なキーが入力コントロールに入力されるのをブロックする場合、イベントが委任されたハンドラーに到達するまでに、イベントが既に処理されているため、委任されたイベント処理ではそれを行うことができません。入力コントロールによって処理されており、その動作に影響を与えるには遅すぎます。

イベントの委任が必要な場合、または有利な場合は次のとおりです。

  • イベントをキャプチャしているオブジェクトが動的に作成/削除され、新しいオブジェクトを作成するたびにイベント ハンドラーを明示的に再バインドすることなくそれらのイベントをキャプチャしたい場合。
  • すべてがまったく同じイベント ハンドラーを必要とするオブジェクトが多数ある場合 (ロットは少なくとも数百です)。この場合、セットアップ時に、100 以上の直接イベント ハンドラーよりも、1 つの委任されたイベント ハンドラーをバインドする方が効率的である可能性があります。委任されたイベント処理は、直接のイベント ハンドラーよりも実行時の効率が常に低いことに注意してください。
  • ドキュメント内の任意の要素で発生するイベントを (ドキュメント内のより高いレベルで) キャプチャしようとしている場合。
  • デザインで明示的にイベント バブリングと stopPropagation() を使用して、ページの問題または機能を解決する場合。

これをもう少し理解するには、jQuery の委任されたイベント ハンドラーがどのように機能するかを理解する必要があります。次のようなものを呼び出すと:

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

オブジェクトにジェネリック jQuery イベント ハンドラーをインストールし#myParentます。クリック イベントがこの委任されたイベント ハンドラーに到達すると、jQuery は、このオブジェクトに関連付けられた委任されたイベント ハンドラーのリストを調べて、イベントの元の要素が委任されたイベント ハンドラーのいずれかのセレクターと一致するかどうかを確認する必要があります。

セレクターはかなり関与する可能性があるため、jQuery は各セレクターを解析し、それを元のイベント ターゲットの特性と比較して、各セレクターと一致するかどうかを確認する必要があります。これは安価な操作ではありません。それらが 1 つしかない場合は大したことではありませんが、すべてのセレクターをドキュメント オブジェクトに配置し、バブルされたすべてのイベントと比較するセレクターが何百もある場合、イベント処理のパフォーマンスが著しく低下する可能性があります。

このため、委任されたイベント ハンドラーをセットアップして、委任されたイベント ハンドラーが実際にターゲット オブジェクトの近くに配置されるようにします。これは、委任された各イベント ハンドラーを通過するイベントが少なくなるため、パフォーマンスが向上することを意味します。委任されたすべてのイベントをドキュメント オブジェクトに配置すると、パフォーマンスが最悪になります。これは、バブリングされたすべてのイベントがすべての委任されたイベント ハンドラーを通過し、すべての委任されたイベント セレクターに対して評価される必要があるためです。これがまさに.live()非推奨の理由です。なぜなら、これが.live()実際に行われたことであり、非常に非効率的であることが判明したからです。


したがって、最適化されたパフォーマンスを実現するには:

  1. 実際に必要な機能を提供する場合、またはパフォーマンスを向上させる場合にのみ、委任されたイベント処理を使用してください。簡単だからという理由だけで常に使用しないでください。実際には必要ないからです。実際には、イベント ディスパッチ時のパフォーマンスは直接イベント バインディングよりも劣ります。
  2. 委任されたイベント ハンドラーを、イベントのソースにできるだけ近い親にアタッチします。イベントをキャプチャする動的要素があるために委任されたイベント処理を使用している場合は、それ自体が動的ではない最も近い親を選択します。
  3. 委任されたイベント ハンドラーには、評価しやすいセレクターを使用します。委譲されたイベント処理のしくみに従えば、委譲されたイベント ハンドラーを多くのオブジェクトと何度も比較する必要があることを理解できます。そのため、できるだけ効率的なセレクターを選択するか、オブジェクトに単純なクラスを追加して、より単純なセレクターを使用できるようにします。委任されたイベント処理のパフォーマンスを向上させます。
于 2012-10-10T17:05:44.247 に答える
9

イベント委任は、要素が実際に DOM に存在する前にハンドラーを記述する手法です。この方法には独自の欠点があるため、そのような要件がある場合にのみ使用してください。

イベント委任はいつ使用する必要がありますか?

  1. 同じ機能を必要とするより多くの要素の共通ハンドラーをバインドする場合。(例: テーブル行ホバー)
    • この例では、直接バインドを使用してすべての行をバインドする必要がある場合、そのテーブルの n 行に対して n ハンドラーを作成することになります。委任メソッドを使用すると、1 つの単純なハンドラーでそれらすべてを処理することになります。
  2. 動的コンテンツを DOM に頻繁に追加する場合 (例: テーブルの行の追加/削除)

イベント委任を使用しない理由

  1. イベント委任は、イベントを要素に直接バインドする場合と比べて遅くなります。
    • ヒットするすべてのバブルでターゲットセレクターを比較します。比較は複雑であるほどコストがかかります。
  2. バインドされている要素に到達するまで、イベントのバブリングを制御することはできません。

PS:コンテンツが DOM に挿入された後にハンドラーをバインドする場合、動的コンテンツの場合でも、イベント委任メソッドを使用する必要はありません。(動的コンテンツが頻繁に削除/再追加されずに追加される場合)

于 2012-10-10T17:32:20.977 に答える
0

どうやら、イベント委任は実際に現在推奨されているようです。少なくともバニラjsの場合。

https://gomakethings.com/why-event-delegation-is-a-better-way-to-listen-for-events-in-vanilla-js/

「Web パフォーマンス # ドキュメント内のすべてのクリックをリッスンするとパフォーマンスが低下するように感じますが、実際には、個々のアイテムに多数のイベント リスナーを配置するよりもパフォーマンスが向上します。」

于 2019-07-26T18:40:00.823 に答える
0

jfriend00の回答にいくつかの発言と反論を追加したいと思います。(ほとんどが私の感覚に基づいた私の意見です)

いいえ - すべての委任されたイベント ハンドラーをドキュメント オブジェクトにバインドしないでください。これはおそらく、作成できる最悪のシナリオです。

まず、イベント委譲によって常にコードが高速化されるわけではありません。有利な場合もあれば、不利な場合もあります。実際にイベントの委任が必要な場合、およびそれによってメリットがある場合は、イベントの委任を使用する必要があります。それ以外の場合は、イベントが発生するオブジェクトにイベント ハンドラーを直接バインドする必要があります。これにより、通常はより効率的になります。

1 つの要素に対してのみ登録してイベントを実行する場合にパフォーマンスがわずかに向上する可能性があることは事実ですが、委任によってもたらされるスケーラビリティの利点に比べれば、それほど重要ではないと思います。また、ブラウザはこれをますます効率的に処理していると思いますが、これを証明するものはありません。私の意見では、イベント委任が最適です。

次に、委任されたすべてのイベントをドキュメント レベルでバインドしないでください。.live() が廃止されたのはまさにこのためです。これは、多くのイベントがこの方法でバインドされている場合に非常に非効率的であるためです。委任されたイベント処理の場合、それらを動的ではない最も近い親にバインドする方がはるかに効率的です。

私はこれに同意します。イベントがコンテナー内でのみ発生することが 100% 確実な場合は、イベントをこのコンテナーにバインドすることは理にかなっていますが、イベントをトリガー要素に直接バインドすることには反対です。

第三に、すべてのイベントが機能するわけではなく、すべての問題が委任によって解決できるわけではありません。たとえば、入力コントロールのキー イベントをインターセプトし、無効なキーが入力コントロールに入力されるのをブロックする場合、イベントが委任されたハンドラーに到達するまでに、イベントが既に処理されているため、委任されたイベント処理ではそれを行うことができません。入力コントロールによって処理されており、その動作に影響を与えるには遅すぎます。

これは単に真実ではありません。このコードペンを参照してください: https://codepen.io/pwkip/pen/jObGmjq

document.addEventListener('keypress', (e) => {
    e.preventDefault();
});

ドキュメントに keypress イベントを登録することで、ユーザーが入力できないようにする方法を示しています。

イベントの委任が必要な場合、または有利な場合は次のとおりです。

イベントをキャプチャしているオブジェクトが動的に作成/削除され、新しいオブジェクトを作成するたびにイベント ハンドラーを明示的に再バインドすることなくそれらのイベントをキャプチャしたい場合。すべてがまったく同じイベント ハンドラーを必要とするオブジェクトが多数ある場合 (ロットは少なくとも数百です)。この場合、セットアップ時に、100 以上の直接イベント ハンドラーよりも、1 つの委任されたイベント ハンドラーをバインドする方が効率的である可能性があります。委任されたイベント処理は、直接のイベント ハンドラーよりも実行時の効率が常に低いことに注意してください。

https://ehsangazar.com/optimizing-javascript-event-listeners-for-performance-e28406ad406cからのこの引用で返信したいと思います

Event delegation promotes binding as few DOM event handlers as possible, since each event handler requires memory. For example, let’s say that we have an HTML unordered list we want to bind event handlers to. Instead of binding a click event handler for each list item (which may be hundreds for all we know), we bind one click handler to the parent unordered list itself.

また、グーグルでperformance cost of event delegation google検索すると、イベント委任を支持するより多くの結果が返されます。

ドキュメント内の任意の要素で発生するイベントを (ドキュメント内のより高いレベルで) キャプチャしようとしている場合。デザインで明示的にイベント バブリングと stopPropagation() を使用して、ページの問題または機能を解決する場合。これをもう少し理解するには、jQuery の委任されたイベント ハンドラーがどのように機能するかを理解する必要があります。次のようなものを呼び出すと:

$("#myParent").on('クリック', 'button.actionButton', myFn); #myParent オブジェクトに一般的な jQuery イベント ハンドラーをインストールします。クリック イベントがこの委任されたイベント ハンドラーに到達すると、jQuery は、このオブジェクトに関連付けられた委任されたイベント ハンドラーのリストを調べて、イベントの元の要素が委任されたイベント ハンドラーのいずれかのセレクターと一致するかどうかを確認する必要があります。

セレクターはかなり関与する可能性があるため、jQuery は各セレクターを解析し、それを元のイベント ターゲットの特性と比較して、各セレクターと一致するかどうかを確認する必要があります。これは安価な操作ではありません。それらが 1 つしかない場合は大したことではありませんが、ドキュメント オブジェクトにすべてのセレクターを配置し、バブルされたすべてのイベントと比較するセレクターが何百もある場合、イベント処理のパフォーマンスが著しく低下する可能性があります。

このため、委任されたイベント ハンドラーをセットアップして、委任されたイベント ハンドラーが実際にターゲット オブジェクトの近くに配置されるようにします。これは、委任された各イベント ハンドラーを通過するイベントが少なくなるため、パフォーマンスが向上することを意味します。委任されたすべてのイベントをドキュメント オブジェクトに配置すると、パフォーマンスが最悪になります。これは、バブリングされたすべてのイベントがすべての委任されたイベント ハンドラーを通過し、すべての委任されたイベント セレクターに対して評価される必要があるためです。これがまさに .live() が廃止された理由です。これは .live() が行ったことであり、非常に非効率的であることが判明したためです。

これはどこに文書化されていますか? それが本当なら、jQuery は非常に非効率的な方法で委譲を処理しているようであり、私の反論はバニラ JS にのみ適用されるべきです。

それでも:この主張を裏付ける公式の情報源を見つけたい.

:: 編集 ::

jQueryは実際に非常に非効率的な方法でイベントバブリングを行っているようです(IE8をサポートしているため) https://api.jquery.com/on/#event-performance

したがって、ここでの私の議論のほとんどは、標準の JS と最新のブラウザーにのみ当てはまります。

于 2020-05-01T11:16:11.497 に答える