JS手动实现事件代理

简介

由于事件在冒泡阶段向上传播到父节点,因此可以把子节点监听的事件在父节点的监听,由父节点的监听函数统一处理多个子元素的的事件,这种方法就叫做事件的代理,处理如下:

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

ul.addEventListener('click', function (event) {
  if (event.target.tagName.toLowerCase() === 'li') {
    // some code
  }
});

这样做的好处:

只需在父节点定义一个监听函数,就可以统一处理子节点的事件了;
动态添加子节点,也可以监听到;

下面呢?我们就手动实现下事件代理

实现步骤

创建一个Delegator类,定义一个on方法(监听),再定义一个destroy方式(用于注销事件)

!function (root, doc) {

  class Delegator {
    /*
     *@param 顶级选择器(代理者)
     */
    constructor (selector) {

    }
    /*
     *绑定事件
     *@param event 绑定事件类型
     *@param selector 需要被代理的选择器
     *@param fn 触发函数
     * */
    on (event, selector, fn) {

    }
    /*
     *移除事件
     */
    destroy () {
   
    }
  }

  root.Delegator = Delegator
}(window, document)

定义两个实例属性,分别用来接受root节点和被代理的元素对象。

 constructor (selector) {
      this.root = document.querySelector(selector);//顶级dom
      this.delegatorEvents = {//代理元素及事件
      	//evenType:[{
			//matcher:selector,//元素dom节点
			//callback:fn,//事件函数
	    }]
	  };
    }

绑定事件具体逻辑

1.事件代理触发顺序满足冒泡规则;;
2.this 应当指向正确的元素;
3.相同事件只做一个监听
4.新添加元素也能够响应绑定;

   constructor (selector) {
      this.root = document.querySelector(selector);//顶级dom
      this.delegatorEvents = {};//代理元素及事件
      //代理逻辑
      this.delegator = e => {        
        let currentNode = e.target;//目标节点
        const targetEventList = this.delegatorEvents[e.type];
        //实现冒泡规则
        while (currentNode !== e.currentTarget) {
          targetEventList.forEach(target => {
            if (currentNode.matches(target.matcher)) {
              //开始委托并把当前目标节点的event对象传过去,改为this指向
              target.callback.call(currentNode, e);
            }
          })
          currentNode = currentNode.parentNode;
        }
      }
    }
    /*
     *绑定事件
     *@param event 绑定事件类型
     *@param selector 需要被代理的选择器
     *@param fn 触发函数
     * */
    on (event, selector, fn) {
     //相同事件只添加一次,如果存在,则再对应的代理事件里添加
      if (!this.delegatorEvents[event]) {
        this.delegatorEvents[event] = [{
          matcher: selector,
          callback: fn
        }]
        this.root.addEventListener(event, this.delegator);
      }else{
        this.delegatorEvents[event].push({
          matcher: selector,
          callback: fn
        })
      }
      return this;
    }

注销事件

    /*
     *移除事件
     */
    destroy () {
      Object.keys(this.delegatorEvents).forEach(eventName => {
        this.root.removeEventListener(eventName, this.delegator)
      });
    }

全部代码

!function (root, doc) {

  class Delegator {
    /*
     *@param 顶级选择器(代理者)
     */
    constructor (selector) {
      this.root = document.querySelector(selector);//顶级dom
      this.delegatorEvents = {};//代理元素及事件
      //代理逻辑
      this.delegator = e => {        
        let currentNode = e.target;//目标节点
        const targetEventList = this.delegatorEvents[e.type];
        //如果当前目标节点等于事件当前所在的节点,不再向上冒泡
        while (currentNode !== e.currentTarget) {
          targetEventList.forEach(target => {
            if (currentNode.matches(target.matcher)) {
              //开始委托并把当前目标节点的event对象传过去
              target.callback.call(currentNode, e);
            }
          })
          currentNode = currentNode.parentNode;
        }
      }
    }
    /*
     *绑定事件
     *@param event 绑定事件类型
     *@param selector 需要被代理的选择器
     *@param fn 触发函数
     * */
    on (event, selector, fn) {
     //相同事件只添加一次,如果存在,则再对应的代理事件里添加
      if (!this.delegatorEvents[event]) {
        this.delegatorEvents[event] = [{
          matcher: selector,
          callback: fn
        }]
        this.root.addEventListener(event, this.delegator);
      }else{
        this.delegatorEvents[event].push({
          matcher: selector,
          callback: fn
        })
      }
      return this;
    }
    /*
     *移除事件
     */
    destroy () {
      Object.keys(this.delegatorEvents).forEach(eventName => {
        this.root.removeEventListener(eventName, this.delegator)
      });
    }
  }

  root.Delegator = Delegator
}(window, document)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值