ホームページ  >  記事  >  ウェブフロントエンド  >  イベントデリゲーションメソッドのJavaScript実装の詳細説明

イベントデリゲーションメソッドのJavaScript実装の詳細説明

韦小宝
韦小宝オリジナル
2018-03-14 16:23:362219ブラウズ

この記事では、イベント委任メソッドの JavaScript実装について説明します。イベント委任メソッドの JavaScript 実装について知らない場合、またはイベント委任メソッドの JavaScript 実装に興味がある場合は、この記事を一緒に見てみましょう。ナンセンスは十分です

基本的な概念

イベント委任とは、平たく言えば、イベント (クリック、キーダウンなど) に応じて 1 つの要素の機能を別の要素に委任することです。 ;

一般的に、1 つまたは複数の要素のイベントは、その親要素または外部要素に委任されます。イベントがバインドされる必要がある要素に応答するとき、それは外部要素です。 will イベント バブリング メカニズムを通じて、外部要素のバインディング イベントでトリガーされ、外部要素で関数を実行します。

例えば、寮の同級生から同時に宅配便が来た場合、全員で順番に受け取る方法と、寮長に任せて1人だけ外出させる方法があります。すべてを受け取り、受信者に応じて各寮生に配布します

ここで、エクスプレスを受け取ることはイベントであり、各学生はイベントに応答する必要がある DOM 要素を参照します。 、速達を受け取りに行く寮長は代理の要素なので、実際にイベントをバインドするのはこの要素です。受取人に応じて速達を配布するプロセスはイベント実行中に決定する必要があります。現在の応答イベントが一致する必要がある 1 つまたは複数のプロキシ要素。

イベントバブリング

前に述べたように、DOM でのイベント委任の実装にはイベントバブリングメカニズムが使用されます。では、イベントバブリングとは何でしょうか?

document.addEventListener でイベント モデルを設定できます: イベント バブリング、イベント キャプチャ、一般的に言えば、イベント バブリング モデルが使用されます

上の図に示すように、イベント モデルは

1、キャプチャ ステージ: イベント バブリング モデルでは、キャプチャ ステージはイベントに応答しません。

2、ターゲット ステージ: ターゲット ステージは、オンのトリガーに対するイベント応答を指します。イベントの最下位要素

3、バブリング フェーズ: バブリング フェーズでは、イベントのトリガー応答が最下層のターゲットから最外層 (ルート ノード) まで層ごとに移動し、イベント プロキシが使用します。イベント バブリング メカニズムは、内側の層が応答する必要があるイベントを外側の層にバインドします。 ### イベント

委任の利点

1. リストがあると想像してみてください。リストには多数のリスト項目があり、リスト項目がクリックされたときにイベントに応答する必要があります

<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
  ......
  <li>item n</li>
</ul>
// ...... 代表中间还有未知数个 li
各リスト項目に関数を 1 つずつバインドすると、メモリの消費量が非常に多くなります。効率の観点からは、多くのパフォーマンスが必要です

したがって、より良い方法は、このクリック イベントを親レイヤー (`ul`) にバインドし、イベントの実行時にターゲット要素を照合して判断することです。

そのため、イベントの委任により、メモリ消費量が大幅に削減され、効率が向上します。

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

たとえば、上記の例では、リスト項目が少数しかなく、各リスト項目にイベントをバインドします。

在很多时候,我们需要通过 AJAX 或者用户操作动态的增加或者去除列表项元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件;

如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的;

所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的。

jQuery 中的事件委托

jQuery 中的事件委托相信很多人都用过,它主要这几种方法来实现:

1、$.on: 基本用法: $('.parent').on('click', 'a', function () { console.log('click event on tag a'); }),它是 .parent 元素之下的 a 元素的事件代理到 $('.parent') 之上,只要在这个元素上有点击事件,就会自动寻找到 .parent 元素下的 a 元素,然后响应事件;

2、$.delegate: 基本用法: $('.parent').delegate('a', 'click', function () { console.log('click event on tag a'); }),同上,并且还有相对应的 $.delegate 来删除代理的事件;

3、$.live: 基本使用方法: $('a', $('.parent')).live('click', function () { console.log('click event on tag a'); }),同上,然而如果没有传入父层元素 $(.parent),那事件会默认委托到 $(document) 上;(已废除)

实现功能

基本实现

比如我们有这样的一个 HTML 片段:

<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
  ......  <li>item n</li></ul>// ...... 代表中间还有未知数个 li

我们来实现把 #list 下的 li 元素的事件代理委托到它的父层元素也就是 #list 上:

// 给父层元素绑定事件document.getElementById(&#39;list&#39;).addEventListener(&#39;click&#39;, function (e) {
  // 兼容性处理
  var event = e || window.event;
  var target = event.target || event.srcElement;
  // 判断是否匹配目标元素
  if (target.nodeName.toLocaleLowerCase === &#39;li&#39;) {
    console.log(&#39;the content is: &#39;, target.innerHTML);
  }});

在上述代码中, target 元素则是在 #list 元素之下具体被点击的元素,然后通过判断 target 的一些属性(比如:nodeName,id 等等)可以更精确地匹配到某一类 #list li 元素之上;

使用 Element.matches 精确匹配

如果改变下 HTML 成:

<ul id="list">
  <li className="class-1">item 1</li>
  <li>item 2</li>
  <li className="class-1">item 3</li>
  ......  <li>item n</li></ul>// ...... 代表中间还有未知数个 li

这里,我们想把 #list 元素下的 li 元素(并且它的 class 为 class-1)的点击事件委托代理到 #list 之上;

如果通过上述的方法我们还需要在 `if (target.nodeName.toLocaleLowerCase === 'li')` 判断之中在加入一个判断 `target.nodeName.className === 'class-1'`;

但是如果想像 CSS 选择其般做更加灵活的匹配的话,上面的判断未免就太多了,并且很难做到灵活性,这里可以使用 Element.matches API 来匹配;

Element.matches API 的基本使用方法: Element.matches(selectorString),selectorString 既是 CSS 那样的选择器规则,比如本例中可以使用 target.matches('li.class-1'),他会返回一个布尔值,如果 target 元素是标签 li 并且它的类是 class-1 ,那么就会返回 true,否则返回 false;

当然它的兼容性还有一些问题,需要 IE9 及以上的现代化浏览器版本;

我们可以使用 Polyfill 来解决兼容性上的问题:

if (!Element.prototype.matches) {
  Element.prototype.matches =
    Element.prototype.matchesSelector ||
    Element.prototype.mozMatchesSelector ||
    Element.prototype.msMatchesSelector ||
    Element.prototype.oMatchesSelector ||
    Element.prototype.webkitMatchesSelector ||
    function(s) {
      var matches = (this.document || this.ownerDocument).querySelectorAll(s),
        i = matches.length;
      while (--i >= 0 && matches.item(i) !== this) {}
      return i > -1;            
    };}

加上 Element.matches 之后就可以来实现我们的需求了:

if (!Element.prototype.matches) {
  Element.prototype.matches =
    Element.prototype.matchesSelector ||
    Element.prototype.mozMatchesSelector ||
    Element.prototype.msMatchesSelector ||
    Element.prototype.oMatchesSelector ||
    Element.prototype.webkitMatchesSelector ||
    function(s) {
      var matches = (this.document || this.ownerDocument).querySelectorAll(s),
        i = matches.length;
      while (--i >= 0 && matches.item(i) !== this) {}
      return i > -1;            
    };}document.getElementById('list').addEventListener('click', function (e) {
  // 兼容性处理
  var event = e || window.event;
  var target = event.target || event.srcElement;
  if (target.matches('li.class-1')) {
    console.log('the content is: ', target.innerHTML);
  }});

函数封装

在应对更多场景上我们可以把事件代理的功能封装成一个公用函数,这样就可以重复利用了。
结合上面的例子来实现一个函数 eventDelegate,它接受四个参数:

1、[String] 一个选择器字符串用于过滤需要实现代理的父层元素,既事件需要被真正绑定之上;

2、[String] 一个选择器字符串用于过滤触发事件的选择器元素的后代,既我们需要被代理事件的元素;

3、[String] 一个或多个用空格分隔的事件类型和可选的命名空间,如 click 或 keydown.click ;

4、[Function] 需要代理事件响应的函数;

这里就有几个关键点:

1、对于父层代理的元素可能有多个,需要一一绑定事件;

2、对于绑定的事件类型可能有多个,需要一一绑定事件;

3、在处理匹配被代理的元素之中需要考虑到兼容性问题;

4、在执行所绑定的函数的时候需要传入正确的参数以及考虑到 this 的问题;

function eventDelegate (parentSelector, targetSelector, events, foo) {
  // 触发执行的函数
  function triFunction (e) {
    // 兼容性处理
    var event = e || window.event;
    var target = event.target || event.srcElement;
    // 处理 matches 的兼容性
    if (!Element.prototype.matches) {
      Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector ||
        function(s) {
          var matches = (this.document || this.ownerDocument).querySelectorAll(s),
            i = matches.length;
          while (--i >= 0 && matches.item(i) !== this) {}
          return i > -1;            
        };
    }
    // 判断是否匹配到我们所需要的元素上
    if (target.matches(targetSelector)) {
      // 执行绑定的函数,注意 this
      foo.call(target, Array.prototype.slice.call(arguments));
    }
  }
  // 如果有多个事件的话需要全部一一绑定事件
  events.split(&#39;.&#39;).forEach(function (evt) {
    // 多个父层元素的话也需要一一绑定
    Array.prototype.slice.call(document.querySelectorAll(parentSelector)).forEach(function ($p) {
      $p.addEventListener(evt, triFunction);
    });
  });}

以上就是本篇文章的所有内容,大家要是还不太了解的话,可以自己多实现两边就很容易掌握了哦!

相关推荐:

JS事件委托实例详解

细说什么是js中的事件冒泡和事件捕获以及事件委托

事件捕获和冒泡以及事件委托对比分析

以上がイベントデリゲーションメソッドのJavaScript実装の詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。