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データベースを監視する方法

推薦する

Linux での MySQL 5.6.33 のインストールと設定のチュートリアル

このチュートリアルでは、LinuxでのMySQL 5.6.33のインストールと設定方法を参考までに紹...

MySQL でコマンドを使用して階層検索を実現する方法の詳細な説明

序文この記事は主にMySQLコマンド階層検索ヘルプの使用に関する内容を紹介します。この記事のサンプル...

CSSは、マウスを線の上に置くと線全体の色を変える効果を実現します。

まとめ:以下のように、CSS で指定した行にマウスを置いたときに行全体の色を変更する方法を示します。...

Baidu Union 環境での広告スキル (グラフィック チュートリアル)

最近、製品部門のユーザーエクスペリエンスチームの学生は、アライアンス環境における広告に関する一連の研...

MySQL/MariaDB ルートパスワードリセットチュートリアル

序文パスワードを忘れることは、よく遭遇する問題です。MySQL または MariaDB データベース...

Nginx プロキシ転送構成を通じてクロスドメイン API プロキシ転送を実装する方法

序文WEB 開発では、クロスドメイン リクエストが頻繁に発生します。クロスドメインの問題を解決する方...

docker pull imageエラーの問題を解決する

説明する: Windows 10 に VM をインストールし、VM で Docker を実行し、Do...

要素 ui の el-table の列にさまざまなスタイルのデータを動的に実装する例

問題の説明Ele.me UI のフレームワークでは、入力データは el-form であり、出力データ...

Vueコンポーネント登録方法の解釈

目次概要1. グローバル登録2. 現地登録3. モジュールシステムへのローカル登録概要コンポーネント...

Docker を使用してスタンドアロン Pulsar とクラスター化された Redis をデプロイする方法 (開発アーティファクト)

目次1. はじめに: 2. ドッカー: 1 カスタムネットワーク2 展開を開始する3 ネットワークを...

Vueのprovideとinjectの使い方と原則を分析する

まず、provide/inject を使用する理由について説明しましょう。祖父コンポーネントと孫コン...

Ubuntu 18仮想マシンのクローン作成後に同じIPアドレスになる問題の解決方法

序文最近、仮想マシンを使用して Ubuntu 18.04 をインストールしました。クローン作成後、I...

CD コマンドを使わずに Linux でディレクトリ/フォルダに入る方法

ご存知のとおり、cd コマンドがないと、Linux でディレクトリを切り替えることはできません。それ...

MySQL で datetime 型のデフォルト値を設定する方法

Navicat クライアントを通じてデフォルトの日時値を変更する際に問題が発生しました。データベース...

JavaScriptプロトタイプとプロトタイプチェーンを徹底的に理解する

目次序文基礎を築くプロトタイプコンストラクタのプロパティ__プロト__プロトタイプチェーン改善する要...