获取元素中的所有监听事件

获取元素中的所有监听事件

Chrome DevTools命令行API提供了多种方式来观察和检查事件监听器。
比如说 getEventListeners方法,可以获取元素上的所有监听器,但是这些方法只能在chrome调试中使用,不能在js中使用。

如果我们想在js中使用,可以修改addEventListenerremoveEventListener

使用call方法复写方法

const addEventListener = document.addEventListener;
document.addEventListener = function () {
  console.log('rewrite addEventListener')
  addEventListener.call(document, arguments[0], arguments[1]);
};

可以在调用addEventListener的时候将事件记录在对应的元素上。其实就是在给元素添加一个属性用来记录所有的事件,然后暴露一些方法用来访问这些事件。

添加属性

创建一个对象,用来保存事件及方法。

class BaseEvent {
  constructor(ele) {
    this.element = ele;
    this._events = {};
    // 复写方法
    this.rewriteAddEventListener();
    this.rewriteRemoveEventListener();
    // 将方法绑定到元素上,即可以通过document.fn进行访问
    ele.getAllEventListeners = this.getAllEventListeners.bind(this);
    ele.getAllEvents = this.getAllEvents.bind(this);
  }
  rewriteAddEventListener() {
    const addEventListener = this.element.addEventListener;
    const _ = this;
    this.element.addEventListener = function () {
      _.addEvent(arguments[0], arguments[1]);
      addEventListener.call(_.element, arguments[0], arguments[1]);
    };
  }
  rewriteRemoveEventListener() {
    const removeEventlistener = this.element.removeEventListener;
    const _ = this;
    this.element.removeEventListener = function () {
      _.removeEvent(arguments[0]);
      removeEventlistener.call(_.element, arguments[0], arguments[1]);
    };
  }
  addEvent(eventName, callback) {
    if (this._events[eventName]) {
      this._events[eventName].push(callback);
    } else {
      this._events[eventName] = [callback];
    }
  }
  removeEvent(eventName) {
    delete this._events[eventName];
  }
  getAllEvents() {
    return Object.getOwnPropertyNames(this._events);
  }
  getAllEventListeners() {
    return this._events;
  }
}

使用:

document._extends = new BaseEvent(document);
document.getAllEventListeners()

复写获取元素的方法

可能有人觉得上边的每次都要new一个BaseEvent才能使用比较繁琐。那么有没有其他更简单的方法。我们试想一下我们是怎么获取对应元素的呢?不就是通过浏览器上的那几个方法嘛(getElementById等),那我们直接复写这些方法就好了,每次在获取对应元素的时候给它们加上一个BaseEvent实例,这样拿到元素之后不就可以直接使用了嘛。

function extendElement(ele) {
  if (!ele) {
    return null;
  }
  if (!ele._extends) {
    ele._extends = new BaseEvent(ele);
    return ele;
  }
}

const getElementById = document.getElementById;
document.getElementById = function () {
  const elem = getElementById.call(document, arguments[0]);
  return extendElement(elem);
};

对于getElementByIdquerySelector而言,获取的只是单个元素,很容易处理。但是对于其他API来讲,获取的是元素数组,这个需要进行额外的处理。

const getElementsByTagName = document.getElementsByTagName;
document.getElementsByTagName = function () {
  const elem = getElementsByTagName.call(document, arguments[0]);
  if (!elem) {
    return new HTMLAllCollection();
  }
  return proxyObject(elem);
};

function proxyObject(obj) {
  const hanlder = {
    get: (obj, prop) => {
      return extendElement(obj[prop]);
    }
  }
  const proxyObject = new Proxy(obj, hanlder);
  return proxyObject;
}

采用Proxy代理拦截对数组的访问,当访问具体元素时候再添加上一个BaseEvent

监听赋值事件

对于document.onclick = function(){}这些事件而已,这边的处理就是监听这些事件,当事件被赋值时,使用addEventListener进行监听。当document.onclick = null时取消监听该事件。

observeAssignmentEvent() {
  const _ = this;
  eventList.forEach((item) => {
    Object.defineProperty(_.element, `on${item}`, {
      set(fn) {
        let i =  _.isAlreadyListenedByAssignment(item);
        if(fn) {
          if( i !== -1) {
            _.element.removeEventListener(item, _._events[item][i])
          }
          _.element.addEventListener(item, {value: fn, intro: 'add by assignment'});
            return;
          }
          if(i !== -1) {
            _.element.removeEventListener(item, _._events[item][i])
          }
        }
      })
   })
 }
isAlreadyListenedByAssignment(item) {
  if(!this._events[item] || this._events[item].length === 0) {
    return -1;
  }
  let idx = -1;
  this._events[item].forEach((item, i) => {
    if(typeof item === 'object') {
      if(item.intro === 'add by assignment') {
        idx = i;
        return;
      }
    }
  })
  return idx;
}

其他API都是类似的处理,这里就不一一介绍了。
具体代码可以查看https://github.com/leopord-lau/eevent

使用

在项目安装eevent:

npm i eevent

在项目中引入import 'eevent'就可以了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值