>  기사  >  웹 프론트엔드  >  이벤트 위임 방법의 JavaScript 구현에 대한 자세한 설명

이벤트 위임 방법의 JavaScript 구현에 대한 자세한 설명

韦小宝
韦小宝원래의
2018-03-14 16:23:362170검색

이 글에서는 JavaScript이벤트 위임 방법 구현에 대해 설명합니다. JavaScript의 이벤트 위임 방법 구현에 대해 모르거나 JavaScript의 이벤트 위임 방법 구현에 관심이 있다면 이 글을 함께 살펴보겠습니다. 요점만 말하자면

기본 개념

이벤트 위임은 이벤트(클릭, 키다운...)에 대한 응답으로 한 요소의 기능을 다른 요소에 위임하는 것입니다. ;

일반적으로 하나 또는 요소 그룹의 이벤트는 상위 요소 또는 외부 요소에 위임됩니다. 이벤트가 실제로 바인딩되어야 하는 요소에 응답하면 외부 요소가 됩니다. 이벤트 버블링 메커니즘을 통해 외부 요소의 바인딩 이벤트에 대해 트리거된 다음 외부 요소에서 함수를 실행합니다.

예를 들어 기숙사 동창에게서 택배가 동시에 도착하면 모두가 하나씩 가져가는 방법도 있고, 기숙사 사감에게 맡기고 한 사람만 내보내는 방법도 있습니다. 그리고 모든 것을 픽업한 다음 수신자에 따라 각 기숙사 학생에게 하나씩 배포합니다.

여기서 익스프레스 픽업은 이벤트이며 각 학생은 이벤트에 응답해야 하는 DOM 요소를 나타냅니다. , 택배를 받으러 나가는 기숙사장은 대리의 요소이므로 실제로 행사를 묶는 것은 이 요소이며, 수령인에 따라 택배를 배분하는 과정은 행사 진행 시에 결정이 필요하다. 현재 응답 이벤트가 일치해야 하는 프록시 요소 중 하나 또는 여러 개.

이벤트 버블링

앞서 언급했듯이 DOM에서 이벤트 위임 구현은 이벤트 버블링 메커니즘을 사용하는데, 이벤트 버블링이란 무엇일까요?

document.addEventListener에서 이벤트 모델을 설정할 수 있습니다. 이벤트 버블링, 이벤트 캡처, 일반적으로 이벤트 버블링 모델이 사용됩니다.

위 그림에 표시된 대로 이벤트 모델은 다음과 같습니다.

1, Capture stage: 이벤트 버블링 모델에서 캡처 단계는 어떤 이벤트에도 응답하지 않습니다.

2, Target stage: 대상 단계는 트리거링에 대한 이벤트 응답을 나타냅니다. 이벤트의 하단 요소

3, 버블링 단계: 버블링 단계는 이벤트의 트리거 응답이 하단 대상에서 가장 바깥쪽 레이어(루트 노드)로 레이어별로 이동하고 이벤트 프록시가 진행되는 것입니다. 이벤트 버블링 메커니즘은 내부 계층이 외부 계층에 응답해야 하는 이벤트를 바인딩합니다. ### 이벤트의 장점

위임

1. 메모리 소비 감소

상상해 보세요. , 목록에 많은 수의 목록 항목이 있고 목록 항목을 클릭할 때 이벤트에 응답해야 합니다.

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

함수가 각 목록 항목에 하나씩 바인딩되면 메모리 소비가 매우 커집니다. 효율성 측면에서 크면 많은 성능이 필요합니다.

따라서 더 좋은 방법은 이 클릭 이벤트를 상위 레이어인 '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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.