JavaScript でのイベント委任について説明してもらえますか? また、どのように役立つのでしょうか?
11 に答える
DOM イベント委譲は、イベント「バブリング」(別名イベント伝播) の魔法によって、各子ではなく単一の共通の親を介して ui イベントに応答するメカニズムです。
要素でイベントがトリガーされると、次のことが発生します。
イベントはそのターゲット
EventTarget
にディスパッチされ、そこで見つかったイベント リスナーがトリガーされます。バブリングEventTarget
イベントは、の親チェーンを上方向に たどることで見つかった追加のイベント リスナーをトリガーし、連続する各 EventTarget に登録されているイベント リスナーをチェックします。この上方への伝播は、 まで続きDocument
ます。
イベント バブリングは、ブラウザーでのイベント委任の基盤を提供します。これで、イベント ハンドラーを 1 つの親要素にバインドできるようになりました。そのハンドラーは、その子ノード(およびその子ノード) でイベントが発生するたびに実行されます。これがイベント委任です。実際の例を次に示します。
<ul onclick="alert(event.type + '!')">
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
この例では、子ノードのいずれかをクリックすると、クリックしたノードにバインドされたクリック ハンドラーがなくても<li>
、アラートが表示されます。それぞれにバインドすると、同じ効果が得られます。"click!"
<li>
onclick="..."
<li>
では、そのメリットは何ですか?
<li>
DOM 操作を介して、上記のリストに新しいアイテムを動的に追加する必要があると想像してください。
var newLi = document.createElement('li');
newLi.innerHTML = 'Four';
myUL.appendChild(newLi);
イベント委任を使用しない"onclick"
場合、イベント ハンドラーを新しい<li>
要素に「再バインド」して、兄弟と同じように動作させる必要があります。イベント委任を使用すると、何もする必要はありません。新しいものをリストに追加するだけ<li>
で完了です。
これは、新しい要素が DOM で動的に作成および/または削除される、多くの要素にバインドされたイベント ハンドラーを持つ Web アプリにとって非常に素晴らしいことです。イベント デリゲーションを使用すると、イベント バインディングを共通の親要素に移動することで、イベント バインディングの数を大幅に減らすことができます。また、その場で新しい要素を動的に作成するコードを、イベント ハンドラーをバインドするロジックから切り離すことができます。
イベント委譲のもう 1 つの利点は、イベント リスナーが使用する合計メモリ フットプリントが減少することです (イベント バインディングの数が減少するため)。頻繁にアンロードする小さなページ (つまり、ユーザーが頻繁に別のページに移動する) には大きな違いはないかもしれません。しかし、寿命の長いアプリケーションの場合、これは重大な問題になる可能性があります。DOM から削除された要素がまだメモリを要求している (つまり、リークしている) 場合、追跡が非常に困難な状況がいくつかあります。多くの場合、このリークされたメモリはイベント バインディングに関連付けられています。イベント委譲を使用すると、イベントリスナーを「アンバインド」するのを忘れるリスクなしに、子要素を自由に破棄できます (リスナーは祖先にあるため)。これらのタイプのメモリ リークを封じ込めることができます (排除されない場合、これを行うのは非常に困難な場合があります。つまり、私はあなたを見ています)。
イベント委譲の具体的なコード例を次に示します。
- JavaScript イベント委任のしくみ
- イベント委任とイベント処理
- jQuery.delegateはイベント委譲+セレクター仕様
- jQuery.onは、2 番目のパラメーターとしてセレクターが渡されると、イベント委任を使用します。
- JavaScript ライブラリを使用しないイベント委任
- Closures vs Event delegation : イベント委任を使用するためにコードを変換しないことの長所を見ていきます
- およびイベント(バブルしない) を委譲する
focus
blur
ために PPK が明らかにした興味深いアプローチ
イベント委任により、特定のノードにイベント リスナーを追加することを回避できます。代わりに、イベント リスナーが 1 つの親に追加されます。そのイベント リスナーは、バブルされたイベントを分析して、子要素の一致を見つけます。
JavaScript の例:
いくつかの子要素を持つ親 UL 要素があるとします。
<ul id="parent-list">
<li id="post-1">Item 1</li>
<li id="post-2">Item 2</li>
<li id="post-3">Item 3</li>
<li id="post-4">Item 4</li>
<li id="post-5">Item 5</li>
<li id="post-6">Item 6</li>
</ul>
また、各子要素がクリックされたときに何かが発生する必要があるとしましょう。個々の LI 要素ごとに個別のイベント リスナーを追加することもできますが、LI 要素がリストから頻繁に追加および削除される場合はどうでしょうか。イベント リスナーの追加と削除は悪夢です。追加と削除のコードがアプリ内の別の場所にある場合は特にそうです。より良い解決策は、イベント リスナーを親 UL 要素に追加することです。しかし、イベント リスナーを親に追加すると、どの要素がクリックされたかをどのように知るのでしょうか?
簡単: イベントが UL 要素に到達したら、イベント オブジェクトの target プロパティをチェックして、実際にクリックされたノードへの参照を取得します。以下は、イベントの委譲を示す非常に基本的な JavaScript スニペットです。
// Get the element, add a click listener...
document.getElementById("parent-list").addEventListener("click", function(e) {
// e.target is the clicked element!
// If it was a list item
if(e.target && e.target.nodeName == "LI") {
// List item found! Output the ID!
console.log("List item ", e.target.id.replace("post-"), " was clicked!");
}
});
まず、クリック イベント リスナーを親要素に追加します。イベントリスナーがトリガーされたら、イベント要素をチェックして、反応する要素のタイプであることを確認します。それが LI 要素の場合、ブーム: 必要なものは揃っています! 必要な要素でない場合、イベントは無視できます。この例は非常に単純です。UL と LI は単純な比較です。もっと難しいことに挑戦してみましょう。多くの子を持つ親 DIV を考えてみましょうが、気になるのは classA CSS クラスを持つ A タグだけです。
// Get the parent DIV, add click listener...
document.getElementById("myDiv").addEventListener("click",function(e) {
// e.target was the clicked element
if(e.target && e.target.nodeName == "A") {
// Get the CSS classes
var classes = e.target.className.split(" ");
// Search for the CSS class!
if(classes) {
// For every CSS class the element has...
for(var x = 0; x < classes.length; x++) {
// If it has the CSS class we want...
if(classes[x] == "classA") {
// Bingo!
console.log("Anchor element clicked!");
// Now do something here....
}
}
}
}
});
dom イベント委任は、コンピューター サイエンスの定義とは別のものです。
これは、表のような親オブジェクトから、表のセルなどの多くの要素からのバブリング イベントを処理することを指します。特に要素を追加または削除するときに、コードをより単純に保つことができ、メモリを節約できます。
委任は、オブジェクトが特定の動作を外部に表現する手法ですが、実際にはその動作を実装する責任を関連付けられたオブジェクトに委任します。これは一見、プロキシ パターンに非常に似ているように思えますが、目的は大きく異なります。委任は、オブジェクト (メソッド) の動作を集中化する抽象化メカニズムです。
一般的に言えば、継承の代わりに委任を使用します。親オブジェクトと子オブジェクトの間に密接な関係が存在する場合、継承は優れた戦略ですが、継承はオブジェクトを非常に密接に結合します。多くの場合、委譲は、クラス間の関係を表現するためのより柔軟な方法です。
このパターンは、「プロキシ チェーン」とも呼ばれます。他のいくつかの設計パターンは委譲を使用します - State、Strategy、および Visitor パターンは委譲に依存します。
委任の概念
1 つの親内に多くの要素があり、それらのイベントを処理したい場合は、各要素にハンドラーをバインドしないでください。代わりに、単一のハンドラーをその親にバインドし、event.target から子を取得します。このサイトは、イベントの委任を実装する方法に関する有用な情報を提供します。 http://javascript.info/tutorial/event-delegation
イベントの委譲を理解するには、まずイベントの委譲が実際に必要な理由と時期を知る必要があります。
多くのケースがあるかもしれませんが、イベント委任の 2 つの大きな使用例について説明しましょう。1. 最初のケースは、関心のある多くの子要素を持つ要素がある場合です。この場合、これらすべての子要素にイベント ハンドラーを追加する代わりに、単純にそれを親要素に追加して決定します。イベントが発生した子要素。
2. イベント委譲の 2 番目の使用例は、ページがロードされたときにまだ DOM にない要素にイベント ハンドラーをアタッチする場合です。もちろん、これは、ページにないものにイベント ハンドラーを追加できないためです。
ページをロードしたときに DOM に 0、10、または 100 個のアイテムのリストがあり、さらに多くのアイテムがリストに追加されるのを待っているとします。そのため、将来の要素にイベント ハンドラーをアタッチする方法がないか、それらの要素がまだ DOM に追加されておらず、さらに多くのアイテムが存在する可能性があるため、それぞれに 1 つのイベント ハンドラーをアタッチするのは役に立ちません。そのうちの。
イベント委任
さて、イベント委任について話すために、実際に話さなければならない最初の概念は、イベント バブリングです。
イベントのバブリング: イベントのバブリングとは、たとえば下の画像のボタンをクリックするなどして、何らかの DOM 要素でイベントが発生またはトリガーされると、まったく同じイベントがすべての親要素でもトリガーされることを意味します。
イベントは最初にボタンで発生しますが、その後、すべての親要素で 1 つずつ発生するため、メイン要素のセクションまでの段落でも発生し、実際には DOM ツリーのずっと上まで発生します。ルートである HTML 要素まで。そのため、イベントは DOM ツリー内でバブルアップすると言い、それがバブリングと呼ばれる理由です。
ターゲット要素:イベントが実際に最初に発生した要素はターゲット要素と呼ばれるため、イベントを発生させた要素はターゲット要素と呼ばれます。上記の例では、もちろん、クリックされたのはボタンです。重要な部分は、このターゲット要素がイベント オブジェクトのプロパティとして格納されることです。これは、イベントが発生するすべての親要素が、イベントのターゲット要素、つまりイベントが最初に発生した場所を認識することを意味します。
イベントが DOM ツリーでバブルアップし、イベントが発生した場所がわかっている場合は、単純にイベント ハンドラーを親要素にアタッチし、イベントがバブルアップするのを待つことができるため、イベント委任につながります。次に、ターゲット要素に対して意図したことをすべて実行します。この手法は、イベント委任と呼ばれます。この例では、イベント ハンドラーをメイン要素に追加するだけです。
繰り返しになりますが、イベント委任とは、関心のある元の要素にイベント ハンドラーを設定するのではなく、それを親要素にアタッチし、基本的に、バブルアップするためそこでイベントをキャッチすることです。次に、対象の要素プロパティを使用して、関心のある要素を操作できます。
例: ここで、ページに 2 つのリスト アイテムがあり、それらのリストにプログラムでアイテムを追加した後、それらから 1 つ以上のアイテムを削除したいとします。イベント委任テクニックを使用すると、目的を簡単に達成できます。
<div class="body">
<div class="top">
</div>
<div class="bottom">
<div class="other">
<!-- other bottom elements -->
</div>
<div class="container clearfix">
<div class="income">
<h2 class="icome__title">Income</h2>
<div class="income__list">
<!-- list items -->
</div>
</div>
<div class="expenses">
<h2 class="expenses__title">Expenses</h2>
<div class="expenses__list">
<!-- list items -->
</div>
</div>
</div>
</div>
</div>
それらのリストにアイテムを追加する:
const DOMstrings={
type:{
income:'inc',
expense:'exp'
},
incomeContainer:'.income__list',
expenseContainer:'.expenses__list',
container:'.container'
}
var addListItem = function(obj, type){
//create html string with the place holder
var html, element;
if(type===DOMstrings.type.income){
element = DOMstrings.incomeContainer
html = `<div class="item clearfix" id="inc-${obj.id}">
<div class="item__description">${obj.descripiton}</div>
<div class="right clearfix">
<div class="item__value">${obj.value}</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>`
}else if (type ===DOMstrings.type.expense){
element=DOMstrings.expenseContainer;
html = ` <div class="item clearfix" id="exp-${obj.id}">
<div class="item__description">${obj.descripiton}</div>
<div class="right clearfix">
<div class="item__value">${obj.value}</div>
<div class="item__percentage">21%</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>`
}
var htmlObject = document.createElement('div');
htmlObject.innerHTML=html;
document.querySelector(element).insertAdjacentElement('beforeend', htmlObject);
}
アイテムを削除:
var ctrlDeleteItem = function(event){
// var itemId = event.target.parentNode.parentNode.parentNode.parentNode.id;
var parent = event.target.parentNode;
var splitId, type, ID;
while(parent.id===""){
parent = parent.parentNode
}
if(parent.id){
splitId = parent.id.split('-');
type = splitId[0];
ID=parseInt(splitId[1]);
}
deleteItem(type, ID);
deleteListItem(parent.id);
}
var deleteItem = function(type, id){
var ids, index;
ids = data.allItems[type].map(function(current){
return current.id;
});
index = ids.indexOf(id);
if(index>-1){
data.allItems[type].splice(index,1);
}
}
var deleteListItem = function(selectorID){
var element = document.getElementById(selectorID);
element.parentNode.removeChild(element);
}
C# のデリゲートは、C または C++ の関数ポインターに似ています。デリゲートを使用すると、プログラマはメソッドへの参照をデリゲート オブジェクト内にカプセル化できます。次に、どのメソッドが呼び出されるかをコンパイル時に知る必要なく、参照されたメソッドを呼び出すことができるコードにデリゲート オブジェクトを渡すことができます。
このリンクを参照してください --> http://www.akadia.com/services/dotnet_delegates_and_events.html
イベント委譲では、見過ごされがちな JavaScript イベントの 2 つの機能、イベント バブリングとターゲット要素を利用します。ボタンのマウス クリックなど、要素でイベントがトリガーされると、その要素のすべての祖先でも同じイベントがトリガーされます。 . このプロセスは、イベント バブリングと呼ばれます。イベントは元の要素から DOM ツリーの一番上にバブルアップします。
10 列と 100 行の HTML テーブルを想像してください。ユーザーがテーブル セルをクリックしたときに何かを実行したいとします。たとえば、クリックしたときにそのサイズのテーブルの各セルを編集可能にする必要があったことがあります。1000 個のセルのそれぞれにイベント ハンドラーを追加すると、パフォーマンスに大きな問題が生じ、ブラウザーをクラッシュさせるメモリ リークの原因となる可能性があります。代わりに、イベント デリゲーションを使用して、テーブル要素にイベント ハンドラーを 1 つだけ追加し、クリック イベントをインターセプトして、クリックされたセルを特定します。
子要素でイベントが発生したときに発生するイベント リスナーを親要素にアタッチします。
イベントの伝播イベントが DOM を介して子要素から親要素に移動するとき、これはEvent Propagationと呼ばれます。これは、イベントが DOM を伝播または移動するためです。
この例では、ボタンからのイベント (onclick) が親段落に渡されます。
$(document).ready(function() {
$(".spoiler span").hide();
/* add event onclick on parent (.spoiler) and delegate its event to child (button) */
$(".spoiler").on( "click", "button", function() {
$(".spoiler button").hide();
$(".spoiler span").show();
} );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<p class="spoiler">
<span>Hello World</span>
<button>Click Me</button>
</p>