美文网首页
封装库-事件绑定

封装库-事件绑定

作者: angryyan | 来源:发表于2017-06-13 19:33 被阅读0次

    问题所在:

    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的源码,看看关于事件绑定这部分的代码。

    相关文章

      网友评论

          本文标题:封装库-事件绑定

          本文链接:https://www.haomeiwen.com/subject/ctqdqxtx.html