#イベントモデル

リスニング機能

ブラウザのイベント モデルは、リスナー関数を通じてイベントに応答します。イベントの発生後、ブラウザはイベントを監視し、対応するリスニング機能を実行します。これは、イベント駆動型プログラミング モード (イベント駆動型) の主なプログラミング方法です。

JavaScript には、リスナー関数をイベントにバインドする 3 つの方法があります。

HTML の on 属性

HTML 言語を使用すると、要素の属性で特定のイベントのリスニング コードを直接定義できます。

<body onload="doSomething()">
<div onclick="console.log('トリガーイベント')">

上記のコードは、body ノードの load イベントと div ノードの click イベントのリスニング コードを指定します。イベントが発生すると、このコードが実行されます。

要素のイベント リスニング属性はすべて「on」にイベント名を加えたものです。たとえば、「onload」は「on +load」で、「load」イベントのリスニング コードを表します。

これらのプロパティの値は関数ではなく、実行されるコードであることに注意してください。

<!-- 正解 -->
<body onload="doSomething()">

<!-- エラー -->
<body onload="doSomething">

指定されたイベントが発生すると、「on-」属性の値が変更されずに JavaScript エンジンに渡されて実行されます。したがって、関数を実行したい場合は、かっこのペアを追加することを忘れないでください。

このメソッドを使用して指定されたリスニング コードは、バブリング フェーズ中にのみトリガーされます。

<div onclick="console.log(2)">
  <button onclick="console.log(1)">クリック</button>
</div>

上記のコードでは、<button><div> の子要素です。 <button>click イベントは、<div>click イベントもトリガーします。 'on-' 属性のリスニング コードはバブリング段階でのみトリガーされるため、クリック結果は最初に 1 を出力し、次に 2 を出力します。つまり、イベントは子要素から親要素へのバブリングを開始します。 。

on- 属性を直接設定すると、要素ノードの setAttribute メソッドを通じて on- 属性を設定するのと同じ効果があります。

el.setAttribute('onclick', 'doSomething()');
// と同等
// <要素 onclick="doSomething()">

要素ノードのイベント属性

要素ノード オブジェクトのイベント属性では、リスニング関数を指定することもできます。

window.onload = 何かをする;

div.onclick = 関数 (イベント) {
  console.log('トリガーイベント');
};

このメソッドを使用して指定されたリスニング機能は、バブリングフェーズ中にのみトリガーされます。

このメソッドと HTML の on- 属性の違いは、完全なリスニング コード (doSomething()) を指定する必要がある後者とは異なり、その値が関数名 (doSomething) であることです。

EventTarget.addEventListener()

すべての DOM ノード インスタンスには、ノードのイベント リスニング関数を定義するために使用される addEventListener メソッドがあります。

window.addEventListener('load', doSomething, false);

addEventListener メソッドの詳細については、「EventTarget」の章を参照してください。

まとめ

上記 3 つの方法 (最初の「HTML on-attribute」) は、HTML コードと JavaScript コードの分離の原則に違反しており、この 2 つを一緒に記述することはコードの分割に適さないため、お勧めできません。

2 番目の「要素ノードのイベント属性」の欠点は、同じイベントに対して 1 つのリスニング関数しか定義できないことです。つまり、「onclick」属性を 2 回定義すると、後の定義が前の定義を上書きします。したがって、その使用はお勧めできません。

3 番目のメソッド EventTarget.addEventListener は、リスニング関数を指定するための推奨される方法です。次のような利点があります。

  • 同じイベントに複数のリスニング機能を追加できます。
  • どの段階(キャプチャ段階またはバブリング段階)でリスニング機能がトリガーされるかを指定する機能。
  • DOM ノードに加えて、他のオブジェクト (windowXMLHttpRequest など) にもこのインターフェースがあり、これは JavaScript 全体の統一リスニング関数インターフェースに相当します。

これを指して

listen 関数内の this は、イベントをトリガーした要素ノードを指します。

<button id="btn" onclick="console.log(this.id)">クリック</button>

上記コードを実行するとクリック後に「btn」が出力されます。

他の 2 つの listen 関数は、this のポインタと同じように記述されます。

// HTMLコードは以下の通り
// <button id="btn">クリック</button>
var btn = document.getElementById('btn');

//書き方その1
btn.onclick = 関数 () {
  console.log(this.id);
};

//書き方2
btn.addEventListener(
  'クリック'、
  関数 (e) {
    console.log(this.id);
  }、
  間違い
);

上記 2 つの記述方法でも、ボタンをクリックした後に「btn」が出力されます。

イベントの伝播

イベントが発生すると、そのイベントは子要素と親要素の間で伝播されます。このスプレッドは 3 つの段階に分かれています。

  • フェーズ 1: window オブジェクトからターゲット ノードへの (上位層から下位層への) 送信は、「キャプチャ フェーズ」と呼ばれます。
  • 第 2 フェーズ: ターゲット ノードでトリガーされ、「ターゲット フェーズ」と呼ばれます。
  • 第 3 フェーズ: window オブジェクトがターゲット ノードから (最下位層から上位層へ) 送信されます。これは、「バブリング フェーズ」と呼ばれます。

この 3 段階の伝播モデルにより、同じイベントを複数のノードでトリガーできます。

<div>
  <p>クリック</p>
</div>

上記のコードでは、<div> ノードの中に <p> ノードがあります。

両方のノードで「クリック」イベントのリスニング関数を設定すると (各ノードのキャプチャ フェーズとバブリング フェーズに 1 つのリスニング関数が設定されます)、合計 4 つのリスニング関数が設定されます。次に、「」をクリックすると、「click」イベントが 4 回トリガーされます。

var フェーズ = {
  1: 「キャプチャ」、
  2: 「ターゲット」、
  3:「バブル」
};

var div = document.querySelector('div');
var p = document.querySelector('p');

div.addEventListener('クリック', コールバック, true);
p.addEventListener('click', コールバック, true);
div.addEventListener('クリック', コールバック, false);
p.addEventListener('click', コールバック, false);

関数コールバック(イベント) {
  var tag = イベント.currentTarget.tagName;
  var フェーズ = フェーズ[event.eventPhase];
  console.log("タグ: '" + タグ + "'.EventPhase: '" + フェーズ + "'");
}

//クリック後の結果
// タグ: 'DIV'。イベントフェーズ: 'キャプチャ'。
// タグ: 'P'。イベントフェーズ: 'ターゲット'。
// タグ: 'P'。イベントフェーズ: 'ターゲット'。
// タグ: 'DIV'。イベントフェーズ: 'バブル'。

上記のコードは、「click」イベントが 4 回トリガーされることを示しています。つまり、「」ノードのキャプチャーフェーズとバブリングフェーズで 1 回ずつ、「」ノードのターゲットフェーズで 2 回です。

  1. キャプチャ フェーズ: イベントが <div> から <p> に伝播すると、<div>click イベントがトリガーされます。
  2. ターゲットステージ: イベントが <div> から <p> に到達すると、<p>click イベントがトリガーされます。
  3. バブリング段階: イベントが <p> から <div> に返されると、再び <div>click イベントがトリガーされます。

このうち、<p> ノードには 2 つのリスニング関数があり (addEventListener メソッドの 3 番目のパラメーターの違いにより 2 つのリスニング関数がバインドされます)、これらは click イベントによって 1 回トリガーされます。 。したがって、<p>target ステージで 2 回出力されます。

ブラウザは常に、「click」イベントのターゲット ノードがクリック位置のネストが最も深いノード (この場合、「」ノード内の「」ノード)であると想定することに注意してください。したがって、「」ノードのキャプチャフェーズとバブリングフェーズが「ターゲット」フェーズとして表示されます。

イベント伝播の最上位オブジェクトは window で、その後に documenthtml (document.documentElement)、および body (document.body) が続きます。つまり、上記の例のイベント伝播の順序は、キャプチャフェーズでは windowdocumenthtmlbodydivp、そしてキャプチャフェーズでは pp です。バブリングフェーズでは、divbodyhtmldocumentwindow

イベントプロキシ

バブリング段階でイベントが親ノードに上向きに伝播するため、子ノードのリスニング関数を親ノード上に定義することができ、親ノードのリスニング関数は複数の子要素のイベントを均一に処理します。このアプローチはイベント委任と呼ばれます。

var ul = document.querySelector('ul');

ul.addEventListener('クリック', 関数 (イベント) {
  if (event.target.tagName.toLowerCase() === 'li') {
    // 何らかのコード
  }
});

上記のコードでは、click イベント リスニング関数が <ul> ノードに定義されていますが、実際には子ノード <li>click イベントを処理します。この利点は、リスナー関数を定義する限り、各 <li> ノードでリスナー関数を定義しなくても、複数の子ノードのイベントを処理できることです。また、将来子ノードを追加した場合でも、リッスン機能は引き続き有効です。

イベントが特定のノードに到達するまで伝播を停止したい場合は、イベント オブジェクトの stopPropagation メソッドを使用できます。

//イベントが p 要素に伝播されると、下方向には伝播しなくなります。
p.addEventListener('click', 関数 (イベント) {
  イベント.stopPropagation();
}、 真実);

// イベントが p 要素にバブルした後は、上方にバブルしなくなります。
p.addEventListener('click', 関数 (イベント) {
  イベント.stopPropagation();
}、 間違い);

上記のコードでは、「stopPropagation」メソッドにより、キャプチャ フェーズとバブリング フェーズでのイベントの伝播がそれぞれ阻止されます。

ただし、stopPropagation メソッドはイベントの伝播を阻止するだけで、イベントが <p> ノードの他の click イベント リスニング関数をトリガーすることを阻止しません。言い換えれば、「クリック」イベントを完全にキャンセルするわけではありません。

p.addEventListener('click', 関数 (イベント) {
  イベント.stopPropagation();
  コンソール.ログ(1);
});

p.addEventListener('click', function(event) {
  // トリガーされます
  コンソール.ログ(2);
});

上記のコードでは、「p」要素は 2 つの「click」イベント リスニング関数にバインドされています。 stopPropagation メソッドはこのイベントの伝播を防ぐことしかできませんが、このイベントをキャンセルすることはできません。そのため、2 番目の listen 関数がトリガーされます。出力は最初に 1、次に 2 になります。

イベントを完全にキャンセルし、後続のクリック リスニング関数をすべてトリガーしないようにするには、stopImmediatePropagation メソッドを使用します。

p.addEventListener('click', 関数 (イベント) {
  イベント.stopImmediatePropagation();
  コンソール.ログ(1);
});

p.addEventListener('click', function(event) {
  // トリガーされません
  コンソール.ログ(2);
});

上記のコードでは、「stopImmediatePropagation」メソッドはこのイベントを完全にキャンセルできるため、後でバインドされるすべての「クリック」リスニング関数はトリガーされなくなります。したがって、2 ではなく 1 のみが出力されます。


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0