著者: Li Juncai (jcLee95) : https://blog.csdn.net/qq_28550263
電子メール : [email protected]
記事アドレス: https://blog.csdn.net/qq_28550263/article/details/132819265
【介绍】:本文介绍前端中的事件委托。
1. 引例
次の質問について考えてみましょう。
- フロントエンド開発でイベント ハンドラーを同様の要素のグループにバインドする必要がある状況に遭遇すると、通常、次のような疑問に直面します。要素ごとに独立したイベント ハンドラーを作成するのは良い考えですか?
次の例を通じて、この問題と委任の概念を説明します。
【問題の説明】:
- 多くの商品が入ったショッピング カートを含む Web ページがあり、各商品に [カートに追加] ボタンがあるとします。
- ユーザーがこれらのボタンをクリックしたときに同じアクション、つまりカートに商品を追加し、カートの表示を更新できるようにしたいと考えています。
最も簡単な方法の 1 つは、次のようにクリック イベント ハンドラーを各ボタンに個別にバインドすることです。
<ul id="cart">
<li>
<span>商品1</span>
<button class="add-to-cart">添加到购物车</button>
</li>
<li>
<span>商品2</span>
<button class="add-to-cart">添加到购物车</button>
</li>
<!-- 更多商品项... -->
</ul>
<script>
const addToCartButtons = document.querySelectorAll('.add-to-cart');
addToCartButtons.forEach(button => {
button.addEventListener('click', function() {
// 执行添加到购物车的逻辑
// 更新购物车显示
});
});
</script>
上記のコードは機能的には可能ですが、潜在的な問題があります。
- ボタンごとに個別のイベント ハンドラーを作成します。
- ご想像のとおり、製品の数が多い場合、同様の関数インスタンスが多数作成され、大量のメモリを消費します。
2. イベントバブリング
イベント バブリング (イベント バブリング) とは、DOM イベントを処理するときに、イベントがトリガーされた最も内側の要素からバブリングを開始し、停止またはキャンセルされるまでルート要素 (通常は or) まで段階的に上向きに伝播することを意味します。 。これは、子要素がイベントをトリガーすると、そのイベントが子要素の親要素、親要素の親要素などにも渡されることを意味します。
イベントバブリングのワークフローは次のとおりです。
- まず、ユーザーは、マウスをクリックしたり画面に触れたりするなど、ページ上の要素でイベントをトリガーします。
- イベントはまず、イベントをトリガーした最も内側の要素 (ターゲット要素)に送出され、バインドされたイベント ハンドラーがその要素上で実行されます。
3. 次に、イベントがバブルアップを開始し、上位の要素に渡って、親要素のレイヤーごとに同じイベントをトリガーします;
4. イベントがドキュメントのルート (通常は要素) に到達すると、イベントのバブルが停止することがあります。または、引き続きブラウザ レベルに渡される場合もあります (イベントがキャンセルされたかどうかに応じて)。
このプロセス中に、ターゲット要素自体の親要素、祖父母要素、曽祖父要素、ドキュメント ルート要素など、任意の親要素にバインドされたイベント ハンドラーがトリガーされることがあります。
3. イベントのバブリングは委任の基礎です。
セクション 1 の場合、小さな欠陥について言及しました。つまり、ボタンごとに個別のイベント処理関数を作成すると、製品数が多い場合には、同様の関数インスタンスが多数作成され、大量のメモリを占有する可能性があります。
このとき、泡立ちのメカニズムを考えます。
- バブリング メカニズムにより、イベントがDOM 構造を介して渡され、複数の要素によってキャプチャされることが可能になります。これにより、親要素上の子要素のイベントをキャプチャできるようになり、イベント ハンドラーの数が減り、パフォーマンスと保守性が向上します。
実際、バブリング メカニズムはイベント委任モデルの基礎でもあります。
セクション 1 のケースと組み合わせると、ユーザーが「カートに追加」ボタンをクリックすると、イベントがバブルアップします。
- 要素では、親要素のイベントを処理し、コードの変更部分を次のように変更できます。
<script>
const cart = document.getElementById('cart');
cart.addEventListener('click', function(event) {
if (event.target.classList.contains('add-to-cart')) {
// 执行添加到购物车的逻辑
// 更新购物车显示
}
});
</script>
変更されたスクリプトとセクション 1 のスクリプトの主な違いは、イベント処理の場所です。
セクション 1 では、イベント ハンドラーは、 を使用して各 [カートに追加] ボタンbutton.addEventListener
に直接バインドされていました。これは、各ボタンに独自の独立したイベント ハンドラーがあり、ユーザーがいずれかのボタンをクリックすると、そのボタンに関連付けられたイベント ハンドラーのみが起動されることを意味します。
addToCartButtons.forEach(button => {
button.addEventListener('click', function() {
// 执行添加到购物车的逻辑
// 更新购物车显示
});
});
変更されたスクリプトでは、イベント ハンドラーがショッピング カート コンテナーにバインドされます。
- 上、中古です
cart.addEventListener
。次に、イベント ハンドラー内で、event.target
イベントをトリガーした要素にクラス名「add-to-cart」が含まれているかどうかがチェックされます。その場合は、対応するロジックを実行します。
const cart = document.getElementById('cart');
cart.addEventListener('click', function(event) {
if (event.target.classList.contains('add-to-cart')) {
// 执行添加到购物车的逻辑
// 更新购物车显示
}
});
この変更の利点は次のとおりです。
- 前者の場合、ボタンごとに独自のイベント ハンドラーがあり、論理的には理解しやすいですが、ボタンの数が多い場合、複数の関数インスタンスが作成されることになり、より多くのメモリを使用する可能性があります。
- 後者では、ショッピング カート コンテナーにバインドされたイベント ハンドラーが 1 つだけあります。イベント ハンドラー インスタンスが 1 つだけであるため、メモリ使用量が削減され、パフォーマンスが向上します。
イベントのバブリング特性を利用し、複数の子要素から親要素に同じ種類のリスナーをマージする後者の方法は、イベント委任と呼ばれます。
4. 別のシナリオ: 動的に生成された要素のイベント バインディング
イベント委任の考え方は、動的に生成された要素のリッスンやイベントの動的バインディングにも適用できます。
動的に生成される要素は、ページが読み込まれた後に JavaScript コードを通じて作成される要素です。これらの要素は初期 HTML には存在しないため、静的要素のようなイベント ハンドラーを直接バインドできません。
イベント委任を使用して動的に生成された要素のイベントをリッスンして処理する方法を、具体的な例を通して詳しく説明します。
クリックするとページ上に新しいリスト項目 (<li> 要素) を動的に作成するボタンがあり、これらの動的に作成されたリスト項目をクリックして、スタイルの変更などのアクションを実行できるようにしたいとします。または他の何かが動作します。コードは以下のように表示されます。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动态元素事件委托案例</title>
</head>
<body>
<ul id="dynamic-list">
<!-- 这里没有任何<li>元素 -->
</ul>
<button id="add-button">添加列表项</button>
<script>
const dynamicList = document.getElementById('dynamic-list');
const addButton = document.getElementById('add-button');
// 点击按钮时,动态创建一个新的列表项
addButton.addEventListener('click', function() {
const newItem = document.createElement('li');
newItem.textContent = '新的列表项';
dynamicList.appendChild(newItem);
});
// 事件委托,监听<ul>上的点击事件
dynamicList.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
// 当点击列表项时,执行操作
event.target.style.backgroundColor = 'lightblue';
}
});
</script>
</body>
</html>
この例には、追加ボタン (addButton) と空の順序なしリスト (dynamicList) があります。
-
ユーザーがボタンをクリックすると、新しいリスト項目 (<li> 要素) が動的に作成され、リストに追加されます。
-
イベント委任を使用してクリック イベントを <ul> 要素にバインドし、すべての <li> 要素のクリック イベントをリッスンします。
動的に生成されたリスト項目がクリックされるたびに、イベントが <ul> 要素まで発生し、イベント ハンドラーはクリックされた要素が <li> 要素であるかどうかを確認し、そうであれば適切なアクションを実行します。この例では、クリックされたリスト項目の背景色を変更します。効果は次の図に示すとおりです。
この例からわかるように、この方法では、リスト項目ごとにイベント ハンドラーを手動でバインドすることなく、イベント ハンドラーを動的にバインドできるため、リスト項目がいくつあっても、コードで処理できます。