自定义事件

事件是可以被 JavaScript 侦测到的行为(事件通常与函数配合使用,当事件发生时函数才会执行)。

事件本质是一种消息,事件模式本质上是观察者模式的实现,那我们先来了解一下观察者模式。

观察者模式

这是一种创建松散耦合代码的技术。它定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

由主体和观察者组成,主体负责发布事件,同时观察者通过订阅这些事件来观察该主体。主体并不知道观察者的任何事情,观察者知道主体并能注册事件的回调函数。

观察者模式示例

function EventTarget(){
  this.handlers = {};
}

EventTarget.prototype = {
  constructor: EventTarget,
  addHandler: function(type, handler){
    if (typeof this.handlers[type] == "undefined"){
      this.handlers[type] = [];
    }
    this.handlers[type].push(handler);
  },
  fire: function(event){
    if (!event.target){
      event.target = this;
    }
    if (this.handlers[event.type] instanceof Array){
      var handlers = this.handlers[event.type];
      for (var i=0, len=handlers.length; i < len; i++){
        handlers[i](event);
      }
    }
  },
  removeHandler: function(type, handler){
    if (this.handlers[type] instanceof Array){
      var handlers = this.handlers[type];
      for (var i=0, len=handlers.length; i < len; i++){
        if (handlers[i] === handler){
          break;
        }
      }
      handlers.splice(i, 1);
    }
  }
};

const events = new EventTarget()
events.addHandler('show', callback1).addHandler('show', callback2)
events.fire({type: 'show'}) //测试用参数
events.removeHandler('show', callback1).fire({type: 'show'})

function callback1() {
  console.log('我是回调函数1')
}

function callback2() {
  console.log('我是回调函数2')
}

大概功能就是:

创建一个事件管理器。handlers是存储事件处理函数的对象。

addHandler:是添加事件的方法,该方法接收两个参数,一个是要添加的事件的类型,一个是这个事件的回调函数名。调用的时候会首先遍历handlers这个对象,看看这个类型的方法是否已经存在,如果已经存在则添加到该数组,如果不存在则先创建一个数组然后添加。

fire:是执行handlers这个对象里面的某个类型的每一个方法。

removeHandler:是相应的删除事件执行函数的方法。

大致了解过观察者模式后,回头看js中自定义事件的写法

var event = new Event('build');

// Listen for the event.
elem.addEventListener('build', function (e) { /* ... */ }, false);

// Dispatch the event.
elem.dispatchEvent(event);

这样的话,elem上通过dispatchEvent方法触发的事件build,只有在elem上注册的监听器能够监听到。

实际情况多数是在一个公共对象如document对象上进行事件的监听和触发。

需要注意的是,当一个事件触发的时候,如果相应的elem及其上级元素没有对应的EventListener,就不会有任何回调操作。

对于子元素的监听,可以对父元素添加事件托管,让事件在事件冒泡阶段被监听器捕获并执行。这时候,使用event.target就可以获取到具体触发事件的元素。

详细例子可以进入传送门了解。

自定义事件的增加和移除

示例:

const btn = document.getElementById("btn")
let switchs = true

function handeler() {
  console.log('logs event trigger')
}

btn.addEventListener('click', function(){
  btn.addEventListener('logs', handeler, false) //监听btn的logs事件
  if(switchs) {
    btn.dispatchEvent(new Event('logs')) //触发btn的logs事件
    switchs = !switchs
  } else {
    console.log('remove event')
    btn.removeEventListener('logs', handeler, false) //移除btn的logs事件
    switchs = !switchs
  }
}, false)
//注意:匿名函数无法通过removeEventListener消除监听事件,实名函数可以