すべてのjqueryイベントを$(document)にバインドする必要がありますか?


164

これはどこから来たのか

私が初めて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/

回答:


215

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

まず、イベントの委任によってコードが高速になるとは限りません。有利な場合もあれば、そうでない場合もあります。イベントの委任が実際に必要な場合、およびその恩恵を受ける場合は、イベントの委任を使用する必要があります。それ以外の場合は、イベントハンドラーを、イベントが発生するオブジェクトに直接バインドする必要があります。これは、一般的に効率的であるためです。

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

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

以下は、イベントの委任が必要または有利な場合です。

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

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

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

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

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

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


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

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

2
驚くばかり。迅速かつ詳細な説明をありがとうございます。.on()を使用するとイベントのディスパッチ時間が増加することは理にかなっていますが、増加したイベントのディスパッチ時間と最初のページ処理チームのどちらを決定するかはまだ苦労していると思います。私は通常、初期ページ時間を短縮するためです。たとえば、ページに200の要素がある場合(およびイベントが発生するとさらに多く)、単一のイベントリスナーをに追加する場合よりも、(100のイベントリスナーを追加する必要があるため)初期ロードで約100倍のコストがかかります。親コンテナリンク
2012年

また、あなたが私を納得させたことを言いたかった-すべてのイベントを$(document)にバインドしないでください。変化しない最も近い親に可能な限りバインドします。イベントの委任がイベントを要素に直接バインドするよりも優れている場合は、ケースバイケースで決定する必要があると思います。そして、動的コンテンツに関連付けられたイベント(変更されない親がないイベント)が必要な場合、@ Vegaが以下で推奨することを続け、コンテンツが挿入された後、単にハンドラーをバインドします( )DOM」、セレクターでjQueryのコンテキストパラメーターを使用します。
2012年

5
@ user1394692-ほぼ同一の要素が多数ある場合は、委任されたイベントハンドラーを使用するのが理にかなっています。私は私の答えでそれを言います。巨大な500行のテーブルがあり、特定の列のすべての行に同じボタンがある場合、すべてのボタンを提供するためにテーブルで1つの委任されたイベントハンドラーを使用します。しかし、200個の要素があり、それらすべてが独自のイベントハンドラーを必要とする場合、200個の委任イベントハンドラーをインストールすることは、200個の直接バインドされたイベントハンドラーをインストールするよりも速くありませんが、委任されたイベントの処理は、イベント実行時にはるかに遅くなる可能性があります。
jfriend00

あなたは完璧です。完全に同意する!これが私ができる最悪のことだと理解してい$(document).on('click', '[myCustomAttr]', function () { ... });ますか?
Artem Fitiskin 2014

すべての要素が(最初のページのロードを除いて)動的に作成/削除されるため、pjax / turbolinksを使用すると興味深い問題が発生します。だから、一度にすべてのイベントをバインドする方が良いdocumentまたは上のより具体的な選択にバインドpage:load上で、これらのイベントおよびunbind page:after-remove?うーん
ドムクリスティ

9

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

イベントの委任はいつ使用すべきですか?

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

イベントの委任を使用すべきではないのはなぜですか?

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

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


0

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

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

「Webパフォーマンス#ドキュメント内のすべてのクリックを聞くことはパフォーマンスに悪影響を与えるように思われますが、実際には、個々のアイテムに多数のイベントリスナーを用意するよりもパフォーマンスが優れています。」


0

jfriend00の回答にいくつかのコメントと反論を追加したいと思います。(主に私の直感に基づく私の意見)

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

まず、イベントの委任によってコードが高速になるとは限りません。有利な場合もあれば、そうでない場合もあります。イベントの委任が実際に必要な場合、およびその恩恵を受ける場合は、イベントの委任を使用する必要があります。それ以外の場合は、イベントハンドラーを、イベントが発生するオブジェクトに直接バインドする必要があります。これは、一般的に効率的であるためです。

単一の要素を登録してイベントを発生させるだけの場合、パフォーマンスが少し向上する可能性があることは事実ですが、委任によってもたらされるスケーラビリティーの利点と比較して、それが重くなることはないと思います。私はまた、これを証明するものはありませんが、ブラウザがこれをますます効率的に処理していると考えています。私の意見では、イベントの委任は行くべき道です!

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

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

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

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

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

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

以下は、イベントの委任が必要または有利な場合です。

イベントをキャプチャしているオブジェクトが動的に作成または削除され、新しいイベントハンドラを作成するたびにイベントハンドラを明示的に再バインドする必要なしに、それらのイベントをキャプチャしたい場合。まったく同じイベントハンドラーが必要なオブジェクトがたくさんある場合(たくさんは少なくとも数百)。この場合、数百以上の直接イベントハンドラーをバインドするよりも、委任された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( 'click'、 'button.actionButton'、myFn); #myParentオブジェクトに汎用jQueryイベントハンドラーをインストールします。クリックイベントがこの委任されたイベントハンドラーに達すると、jQueryはこのオブジェクトにアタッチされた委任されたイベントハンドラーのリストを調べ、イベントの元の要素が委任されたイベントハンドラーのセレクターのいずれかに一致するかどうかを確認する必要があります。

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

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

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

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

::編集::

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.