封装库-事件绑定

问题所在:

1 支持同一个元素的同一个事件句柄上可以绑定多个坚挺函数;
2 如果在同一个元素的同一个事件句柄上多次注册同一个函数,那么除第一次注册的函数,其余都将被忽略;
3 函数体内的this指向的应当是正在处理事件的节点(IE指向window);
4 监听函数的执行顺序应当是按照绑定的顺序执行;
5 在函数体内不用使用 event = event || window.event,来标准化Event对象。

var btn = document.getElementById("btn"); btn.onclick = function () { alert('我是传统事件绑定'); }
以上代码展示的传统的事件绑定。现代绑定中W3C使用的是:addEventListener和removeEventListener.IE使用的是attachEvent和detachEvent.

    // 跨浏览器添加事件
     function addEvent(obj, type, fn) {
          if (typeof addEventListener != 'undefined')
               {
                 obj.addEventListener(type, fn, false);
                } else if (typeof attachEvent != 'undefined') 
                {
                   obj.attachEvent('on' + type, fn);
                }
           }```
  //跨浏览器删除事件
  function removeEvent(obj, type, fn) {
      if (typeof removeEventListener != 'undefined')
          {
             obj.removeEventListener(type, fn);
          } else if (typeof detachEvent != 'undefined') 
            {
                obj.detachEvent('on' + type, fn);
            }
      }
以上的两个函数解决了:***同时绑定多个函数以及标准化event的问题***。但是*IE多次注册同一函数*,第一个除外的其他函数并未被忽略;IE中监听函数的执行并不是按照绑定的顺序进行执行,而是*倒序执行*。还有一个问题就是IE中*this传递过来的是window*,而不是当前正在运行事件句柄的节点。
为了解决this传递问题,我们需要使用匿名函数+传递参数的方式来解决:
`  obj.addEvent('on' + type,function(){
    fn.call(obj,window.event);
  });`
使用call第一个参数就是获取this,从第二个参数开始,可以通过函数参数获取。故以上代码解决了IE中this指向window的问题,同时可以获取到event对象。
但是这又引入了新的问题:***无法删除事件  无法顺序执行  IE的现代绑定存在内从泄露的问题。***

从上述问题我们可以看到主要问题是在IE的attachEvent上,存在着无法避免地内佛那个泄露问题。所以我们考虑使用传统事件绑定对IE进行封装。
//跨浏览器添加事件绑定
function addEvent(obj, type, fn) {
    if (typeof obj.addEventListener != 'undefined') {
        obj.addEventListener(type, fn, false);
    } else {
          //创建一个可以保存事件的哈希表(散列表)
           if (!obj.events) obj.events = {};
           if (!obj.events[type]) {
                //创建一个可以保存事件处理函数的数组
              obj.events[type] = [];
              //存储第一个事件处理函数
              if (obj['on' + type])  obj.events[type][0] = fn;
            }
            //通过事件计数器来从第二个事件处理函数开始
            obj.events[type][addEvent.ID++] = fn;
             //执行所有事件处理函数
            obj['on' + type] = function () {
            for (var i in obj.events[type]) {
                obj.events[type][i]();
              }
          }
      }
  }

 //每个事件分配一个ID 计数器
 addEvent.ID = 1;
//事件处理函数调用
addEvent.exec = function (event) {
    var e = event || addEvent.fixEvent(window.event);
    var es = this.events[e.type];
    for (var i in es) {
        es[i].call(this, e);
    }
 };

//获取IE 的event,兼容W3C 的调用
addEvent.fixEvent = function (event) {
    event.preventDefault = addEvent.fixEvent.preventDefault;
    event.stopPropagation = addEvent.fixEvent.stopPropagation;
    return event;
};

//兼容IE 和W3C 阻止默认行为
addEvent.fixEvent.preventDefault = function () {
    this.returnValue = false;
};

//兼容IE 和W3C 取消冒泡
addEvent.fixEvent.stopPropagation = function () {
    this.cancelBubble = true;
};

//跨浏览器删除事件
function removeEvent(obj, type, fn) {
    if (typeof obj.removeEventListener != 'undefined') {
        obj.removeEventListener(type, fn, false);
    } else {
        var es = obj.events[type];
        for (var i in es) {
            if (es[i] == fn) {
            delete obj.events[type][i];
            }
        }
     }
  }
以上就是基于原声JS自己封装的事件绑定库。后续的话需要研究下jQuery的源码,看看关于事件绑定这部分的代码。

你可能感兴趣的:(封装库-事件绑定)