JavaScript イベント キャプチャ バブリングとキャプチャの詳細

JavaScript イベント キャプチャ バブリングとキャプチャの詳細

1. イベントの流れ

JavaScriptでは、イベント フローはDOMイベント フローを指します。

1. コンセプト

イベント伝播プロセスはDOMイベント フローです。
イベント オブジェクトがDOM内で伝播するプロセスは、「イベント フロー」と呼ばれます。
たとえば、コンピューターの電源を入れるには、まずコンピューターを見つけ、次に電源ボタンを見つけ、最後に手動で電源ボタンを押す必要があります。コンピュータの電源をオンにするイベントを完了します。このプロセス全体をイベント フローと呼びます。

2. DOMイベントフロー

DOM イベントにもプロセスがあります。イベントのトリガーからイベントの応答までには 3 つの段階があります。

  • イベントキャプチャフェーズ
  • 目標段階で
  • イベントバブリングフェーズ

上記の例では、コンピューターの電源を入れるプロセスは、 JavaScriptのイベントフローのようなものです。電源ボタンを見つけるプロセスは、イベントキャプチャのプロセスです。電源ボタンを見つけたら、手で電源ボタンを押します。手で電源ボタンを押すことを選択するプロセスは、ターゲットステージにあります。電源ボタンを押すと、コンピューターが起動し始め、これがイベントのバブリングです。 順序は最初にキャプチャし、次にバブリングします。

イベント ソーシングについて理解できたので、次は 3 つのプロセスを見てみましょう。

イベントキャプチャ:

注:イベント キャプチャは古いブラウザー (IE8 以下) ではサポートされていないため、実際にはイベント ハンドラーは通常、バブリング フェーズ中にトリガーされます。

イベント キャプチャは、イベント フローの最初のステップです。
DOM イベントがトリガーされると (DOM イベントをトリガーする要素はイベント ソースと呼ばれます)、ブラウザーはルート ノードから内部にイベントを伝播します。つまり、イベントはドキュメントのルート ノードからターゲット オブジェクト ノードに流れます。途中でさまざまなレベルの DOM ノードを通過し、最終的にターゲット ノードに到達してイベントのキャプチャを完了します。

ターゲットフェーズ:

イベントがターゲット ノードに到達すると、イベントはターゲット フェーズに入ります。イベントはターゲット ノードでトリガーされます。
つまり、イベントは、イベントをトリガーする最下位レベルの要素に伝播されます。

イベントバブリング:

イベント バブリングは、イベント キャプチャの逆の順序で行われます。イベントキャプチャの順序は外側から内側へ、イベントバブリングの順序は内側から外側へです。
イベントがターゲット ステージに伝播されると、ターゲット ステージの要素は受信した時間を上方に伝播します。つまり、イベントによってキャプチャされたパスに沿って逆方向に伝播し、要素の祖先要素まで段階的に上方に伝播します。ウィンドウオブジェクトまで。

例を見てみましょう。box3 をクリックすると、box2 と box1 のクリック イベントがトリガーされます。

<!DOCTYPE html>
<html>

<ヘッド>
    <メタ文字セット="UTF-8">
    <title>JavaScript イベント バブリング</title>
</head>
<スタイル タイプ="text/css">
    #box1 { 背景: 青紫;}
    #box2 {背景: アクアマリン;}
    #box3 {背景: トマト;}
    div { パディング: 40px; マージン: 自動;}
</スタイル>

<本文>
    <div id="box1">
        <div id="box2">
            <div id="box3"></div>
        </div>
    </div>
    <スクリプト>
        window.onload = 関数(){
            定数 box1 = document.getElementById('box1')
            定数 box2 = document.getElementById('box2')
            定数 box3 = document.getElementById('box3')
            box1.onclick = sayBox1;
            box2.onclick = sayBox2;
            box3.onclick = sayBox3;
            関数 sayBox3() {
                console.log('最も内側のボックスをクリックしました');
            }
            関数 sayBox2() {
                console.log('中央のボックスをクリックしました');
            }
            関数 sayBox1() {
                console.log('一番外側のボックスをクリックしました');
            }
        }
    </スクリプト>
</本文>

</html>


現時点では、クリックキャプチャの伝播順序は次のとおりです。
ウィンドウ -> ドキュメント -> <html> -> <body> -> <div #box1> -> <div #box2> -> <div #box3>
現時点では、クリックバブリングの伝播順序は次のとおりです。
<div #box3> -> <div #box2> -> <div #box1> -> <body> -> <html> -> ドキュメント -> ウィンドウ

最新のブラウザはすべて、 windowオブジェクトからイベントのキャプチャを開始し、バブリングの最終停止点もwindowオブジェクトです。 IE8 以下のブラウザでは、 documentオブジェクトにのみバブルします。
イベントバブリング:ページ内の要素の位置ではなく、要素のHTML構造によって決定されるため、要素が配置やフローティングによって親要素の範囲外になった場合でも、要素をクリックするとバブリング現象が発生します。

イベント フローの 3 つの段階がわかったところで、この機能を使って何ができるでしょうか?

2. イベントの委任

<ul>タグの下に多数の <li> タグがあり、 onclickイベントをすべての<li>タグにバインドする必要があるシナリオを想像してください。この問題はループで解決できますが、もっと簡単な方法はあるでしょうか?
これらの<li>の共通の親要素<ul>onclickイベントを追加できます。すると、内部の任意の<li>タグがonclickイベントをトリガーすると、 onclickイベントはバブリング メカニズムを通じて<ul>に伝播され、処理されます。この動作はイベント委任と呼ばれ、 <li>イベントバブリングを使用してイベントを<ul>に委任します。
イベント キャプチャを使用してイベントを委任することもできます。使い方は同じですが、順序が逆になります。

  <ul id="myUl">
    <li>項目 1</li>
    <li>項目 2</li>
    <li>項目 3</li>
    ...
  </ul>


まだ少しわかりにくいかもしれません。簡単に言うと、イベントバブリングを使用して要素のイベントをその親に委任することです。

実生活の例を挙げると、11 月には速達便が届きます。通常、宅配業者は宅配便を各家庭に戸別配達しますが、これは非効率的です。宅配業者は、コミュニティ内のすべての速達便をコミュニティ内の宅配ステーションに置き、速達便の配達を委託するというアイデアを思いつきました。コミュニティ内の受取人は、ピックアップ コードを使用して宅配ステーションに行き、速達便を受け取ることができます。
ここで、荷物を配達する宅配業者はイベントであり、受取人はイベントに応答する要素であり、郵便局はプロキシ要素に相当します。受取人は受領コードを使用して郵便局に荷物を受け取りに行き、これがイベント実行です。プロキシ要素は、現在の応答イベントがトリガーされた特定のイベントと一致するかどうかを判断します。

しかし、これを行うことでどのようなメリットがあるのでしょうか?

1. イベント委託のメリット

イベント委任には2つの利点がある

  • メモリ使用量を削減
  • 動的にイベントをバインドする

メモリ消費量を削減し、ページパフォーマンスを最適化します

JavaScriptでは、各イベント ハンドラはオブジェクトであり、オブジェクトはページ メモリを占有します。メモリ内のオブジェクトが増えるほど、ページのパフォーマンスは低下します。また、 DOM操作により、ブラウザはページを再配置して再描画します (この点が明確でない場合は、ページ レンダリング プロセスについて学習できます)。DOM 操作が多すぎると、ページのパフォーマンスに影響します。パフォーマンス最適化の主な考え方の 1 つは、リフローと再描画を最小限に抑えること、つまりDOM操作を減らすことです。

上記のonclickイベントを<li>タグにバインドする例では、イベント委任を使用すると、各 <li> タグに関数をバインドする必要がなくなります。<ul> タグに 1 回バインドするだけで済みます。li タグが多数ある場合、これによりメモリ消費が大幅に削減され、効率が向上します。

イベントを動的にバインドする:

子要素が不確実であるか動的に生成される場合は、子要素を監視する代わりに親要素を監視することができます。
上記のonclickイベントを<li>タグにバインドする例では、多くの場合、これらの<li>タグの数は固定されておらず、ユーザー操作に基づいて一部の<li>タグが追加または削除されます。タグを追加または削除するたびに、新しく追加または削除された要素に対応するイベントを再バインドまたはバインド解除する必要があります。

イベント委譲を使用すると、各<li>を操作する必要がなくなります。 <ul>に一度バインドするだけで済みます。イベントは<ul>にバインドされているため、 <li>要素は<ul>に影響を与えることはできません。 <li>要素の実行は、イベント関数の実行に実際に応答するプロセスで一致します。したがって、イベント委譲を使用すると、イベントを動的にバインドするときに多くの反復作業を削減できます。

イベント委任の利点がわかったところで、それをどのように使用すればよいのでしょうか?

2. イベント委任の使用

イベント委任を使用するには、イベント監視用のaddEventListener()メソッドが必要です。
このメソッドは、関数を呼び出すオブジェクトに指定されたリスナーを登録します。オブジェクトが指定されたイベントをトリガーすると、指定されたコールバック関数が実行されます。

使用法:

要素にイベントリスナーを追加します(イベントタイプ、関数、キャプチャの使用);


パラメータ必須/オプション説明する
イベントタイプ必須イベントの種類を指定します。
関数必須イベントがトリガーされた後のコールバック関数を指定します。
キャプチャを使用するオプションイベントがキャプチャ フェーズで実行されるか、バブリング フェーズで実行されるかを指定します。

3番目のパラメータuseCaptureはブール型で、デフォルト値はfalseです。

  • true -イベントがキャプチャフェーズ中に実行されることを示します
  • false-イベントがバブリングフェーズで実行されることを示します

次の例を見てください。

<!DOCTYPE html>
<html>

<ヘッド>
  <メタ文字セット="UTF-8">
  <title>JavaScript イベント委譲</title>
</head>

<本文>

  <ul>
    <li>項目 1</li>
    <li>項目 2</li>
    <li>項目 3</li>
    <li>項目 4</li>
  </ul>

  <スクリプト>
    const myUl = document.getElementsByTagName("ul")[0];

    myUl.addEventListener("クリック", myUlFn);

    関数 myUlFn(e) {
      if (e.target.tagName.toLowerCase() === 'li') { // クリックする要素かどうかを判断します console.log(`You clicked ${e.target.innerText}`);
      }
    }

  </スクリプト>
</本文>

</html>

注意:これは一般的なイベント委譲方法ですが、この記述方法には問題があります。つまり、_<li>_ に子要素がある場合、この子要素をクリックしてもイベントはトリガーされません。この質問は落とし穴です。

イベント バブリングは、非常に便利な場合もありますが、煩わしい場合もあります。必要がない場合はキャンセルできますか?

3. イベントのバブリングとキャプチャを禁止する

注: focus、blur、change、submit、reset、select などのすべてのイベントがバブルするわけではありません。

バブリングとキャプチャを無効にするには、 stopPropagation()。
stopPropagation()キャプチャ フェーズとバブリング フェーズで現在のイベントのそれ以上の伝播を停止します。
これは、イベントのバブリングを防ぐためのメソッドです。バブリングは発生しますが、このメソッドを呼び出すと、デフォルトのイベントは実行されます。
aタグをクリックすると、 aタグがジャンプします。

戻り値やパラメータがないため、使い方も非常に簡単です。

イベントの伝播を停止します。


上記のイベント バブリングの例を少し変更した次の例を参照してください。

    <div id="box1">
        <div id="box2">
            <div id="box3"></div>
        </div>
    </div>
    <スクリプト>
        定数 box1 = document.getElementById('box1')
        定数 box2 = document.getElementById('box2')
        定数 box3 = document.getElementById('box3')
        box1.onclick = sayBox1;
        box2.onclick = sayBox2;
        box3.onclick = sayBox3;
        関数 sayBox3() {
            console.log('最も内側のボックスをクリックしました');
        }
        関数 sayBox2(e) {
            console.log('中央のボックスをクリックしました');
            e.stopPropagation(); //イベントキャプチャとバブリングを無効にする}
        関数 sayBox1() {
            console.log('一番外側のボックスをクリックしました');
        }
    </スクリプト>

イベントがbox2にバブルすると、関数sayBox2が呼び出され、バブルを停止するためにe.stopPropagation();が呼び出されます。

JavaScript イベント キャプチャ バブリングとキャプチャの詳細に関するこの記事はこれで終わりです。JavaScript イベント キャプチャ バブリングとキャプチャに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

IV. 参考文献

MDN 中国語版 https://developer.mozilla.org/zh-CN/
知乎 https://zhuanlan.zhihu.com/p/26536815

以下もご興味があるかもしれません:
  • 最もよく使用されるJavaScriptイベントについて詳しく学ぶ
  • JavaScriptのイベントループの仕組みの分析
  • jsイベント委譲の詳細な説明
  • js におけるイベントバブリングとイベントキャプチャの簡単な分析
  • JavaScript イベント ループのケース スタディ
  • Javascript イベントキャプチャとバブリングメソッドの詳細な説明

<<:  訪問者を惹きつけるウェブサイトコンテンツを作成する14の方法

>>:  Zabbix Agent2を使用してOracleデータベースを監視する方法

推薦する

Vue プロジェクトで mock を使用する方法をご存知ですか?

目次最初のステップ: 2 番目のステップは、request.js で関連する構成を行うことです。re...

Linux parted ディスク パーティション実装手順の分析

fdisk と比較すると、parted はあまり使用されず、主に 2T を超えるパーティションに使用...

MySQL サブクエリ (ネストされたクエリ)、結合テーブル、複合クエリの詳細な説明

1. サブクエリMySQL 4.1以降はサブクエリをサポートしていますサブクエリ:別のクエリ内にネス...

Nginx 静的サービス設定の詳細な説明 (ルートとエイリアスの指示)

静的ファイルNginx は高いパフォーマンスで知られており、フロントエンドのリバース プロキシ サー...

MySQL Order By 複数フィールドのソートルールのコード例

事前に言っておく気まぐれですが、MySQL の order by sorting にどのようなルール...

MySQLデータベース操作の基本コマンド

1. データベースを作成します。 データ data _name を作成します。 PHP でデータベー...

Docker で Kong API Gateway をインストールして使用する詳細なチュートリアル

1 はじめにKong は単純な製品ではありません。この記事で言及されている Kong は主に Kon...

Linux のユーザーとグループ管理によく使われるコマンドの概要

この記事では、Linux のユーザーとグループの管理によく使用されるコマンドをまとめます。ご参考まで...

VMware ESXi CLI の一般的なコマンドを調べる

目次【共通コマンド】 [一般的な esxi コマンドの概要] [esxcli コマンドの調査] ES...

プロトタイプとプロトタイプチェーン プロトタイプとプロトタイプの詳細

目次1. プロトタイプ2. プロトタイプチェーン2.1 コンストラクタ2.2 電話をかける/申し込む...

MySQL 8.0.18 インストール構成の最適化チュートリアル

MySQLのインストール、設定、最適化は参考用です。具体的な内容は次のとおりです。 MySQL ダウ...

JavaScript のスプレッド演算子とレスト演算子の違いの詳細な説明

目次レスト演算子とは何ですか? JavaScript 関数では REST 演算子はどのように機能しま...

Dockerfile を使用して SpringBoot プロジェクトをデプロイする方法

1. SpringBoootプロジェクトを作成し、jarパッケージにパッケージ化する2. Linux...

HTML におけるいくつかの特殊属性タグの使用法の紹介

以下の属性はブラウザとの互換性があまりありません。 1.transform:rotate(45度) ...

Vue の自動書式設定の改行保存の詳細な説明

ネットで変更方法をいろいろ調べたのですが、うまくいきませんでした。後で大物から見て削除しました。フォ...