zoukankan      html  css  js  c++  java
  • jQuery的事件模型

    前几天自己着重读了jQuery1.11.1的源码,又结合了之前对DE事件模型的分析,最后也实现一个简陋的事件模型。

    jQuery的事件系统离不开jQuery的缓存系统。

    jQuery的第一代缓存是直接将数据存储在 缓存体 这个数据结构中,但是需要在元素上添加一个uuid来作为标示,

    标记在缓存体中的位置。但是仔细想想,就会发现,如果对window或者document进行事件侦听,就会在这两个

    对象上添加额外属性,会造成全局污染,不是很好。

    所以jQuery第二代缓存系统应运而生,这次不对元素进行添加属性,而是判断元素的valueOf()方法的返回值,如果没有返回值是

    对象,则说明缓存体中并没有该元素的缓存数据,进而使用ECMA5的Object.defineProperty来对valueOf方法进行重写,并返回

    一个guid,作为访问缓存体的钥匙。

    简单讲述了缓存系统,现在着重讲解下jQuery的事件系统:

    主要使用了几个数据结构,即元素的缓存体,Event构造函数,和Handler构造函数。

      当使用bind(el,type,fn)添加回调时,会根据Handler构造函数构造一个handler实例,在我的具体实现中,参数fn可能是一个函数,也可能

    是一个对象,若是对象,则标记这个回调函数的功能--once函数或者throttle函数或delay函数。 其次就是对fn的封装,在库中,fn的包装函数

    实现了新事件对象的创建,以及对新创建的事件对象的修补,并调整了在回调中this的指向。最后将该handlerObj存入该元素对应的缓存体中,

    并用addEvent绑定事件。

      使用unbind移除回调也比较简单,无非是移除缓存,移除回调。

      trigger触发回调主要就是传入参数的处理,执行带有参数的回调。

      

    现附上简单的实现:

      1         // HandlerObject constructor
      2         function Handler(config){
      3             this.handler = config.handler;
      4             this.special = config.special; //特殊的回调,ex. once函数,throggle函数等等,原回调放在此处,handler放包裹后的回调
      5             this.type = config.type;
      6             this.namespace = config.namespace;
      7             this.data = config.data;
      8             this.once = config.once;
      9             this.delay = config.delay;
     10             this.throttle = config.throttle;
     11             this.stop = config.stop;  //  取消默认和冒泡
     12             this.stopBubble = config.stopBubble;
     13             this.preventDefalut = config.preventDefalut;
     14         }
     15         //typeEvents=[handlerObj1,handlerObj2,...]
     16         function execHandlers(el,event,args,context){  // 若args不为空,则为自定义事件出发,trigger
     17             if(el.nodeValue == 3 || el.nodeValue == 8) return;
     18             var elData,events,handlers,typeEvents,ret,
     19                 flag = true;
     20             context = context || el;
     21             //获取缓存对象
     22             elData= S._data(el);
     23             if(!elData || !elData['events'])return;
     24             events = elData['events'];
     25             handlers = elData['handlers'];
     26             if(!events[event.type])return;
     27             typeEvents = events[event.type];
     28 
     29             // 如果其中一个回调执行出错,函数库也不会抛错
     30             for(var i = 0,len=typeEvents.length;i<len;i++){
     31 
     32                 // 捕获错误,如果一个事件绑定多个回调,其中一个回调出错不会影响其他回调执行
     33                 try{
     34                     // 如果设置var isImmediatePropagationStopped,那么执行两件事:
     35                     //  1,停止执行该元素同事件的其他处理函数
     36                     //  2,停止冒泡
     37                     if(event.isImmediatePropagationStopped()) break;
     38                     ret = execHandler(el,event,typeEvents[i],args,context);
     39                     if(ret == false){
     40                         flag = false
     41                     }
     42                 }catch(e){
     43                     setTimeout(function(){
     44                         throw Error(e);   // 异步抛出错误
     45                     },0);
     46 
     47                     if(i < len && i+1 <len){
     48                         i++;
     49                         ret = execHandler(el,event,typeEvents[i],args,context);
     50                         if(ret == false){
     51                             flag = false
     52                         }
     53                     }
     54                 }
     55             }
     56             if(!flag){
     57                 event.preventDefault();
     58                 event.stopPropagation();
     59             }
     60             return;
     61         }
     62         function execHandler(el,event,handlerObj,args,context){
     63             var handler = handlerObj.handler,
     64                 type = event.type,
     65                 special = handlerObj.special,
     66                 stop = handlerObj.stop,
     67                 preventDefault = handlerObj.preventDefalut,
     68                 stopBubble = handlerObj.stopBubble,
     69                 data = handlerObj.data,
     70                 once = handlerObj.once,
     71                 delay = handlerObj.delay,  // 时延
     72                 throttle = handlerObj.throttle; //最小间隔时间
     73             if(handlerObj.type && type !== handlerObj.type) return;
     74 
     75             if(!handler || !S.isFunction(handler))return;
     76 
     77             if(stop){
     78                 event.preventDefalut();
     79                 event.stopPropagation();
     80             }
     81             if(preventDefault){
     82                 event.preventDefalut();
     83             }
     84             if(stopBubble){
     85                 event.stopPropagation();
     86             }
     87 
     88 
     89             if(once){
     90                 var onceHandler = function(event,args){
     91                     return S.once(handler,context,event,args);
     92                 };
     93                 return onceHandler.call(context,event,args);
     94             }
     95             if(delay && S.isNumber(delay)){
     96                 var delayHandler = function(event,args){
     97                     return S.delay(handler,context,delay,event,args);
     98                 }
     99                 return delayHandler.call(context,event,args);
    100             }
    101             if(throttle && S.isNumber(throttle)){
    102                 var throttleHandler = function(event,args){
    103                     return S.throttle(handler,context,throttle,event,args);
    104                 }
    105                 return throttleHandler.call(context,event,args);
    106             }
    107 
    108             if(handler){
    109                 return handler.call(context,event,args);
    110             }
    111             return;
    112         }
    113 
    114         function returnTrue(){
    115             return true;
    116         }
    117         function returnFalse(){
    118             return false;
    119         }
    120         //Event constructor
    121         function Event(e){ //传入事件参数
    122             this.originalEvent = e;
    123             this.isPreventDefault = returnFalse;
    124             this.isStopPropagation = returnFalse;
    125             this.isImmediatePropagationStopped = returnFalse;
    126             var type = e.type;
    127             if(/^(w+).(w+)$/.test(type)){
    128                 this.type = RegExp.$1;
    129                 this.namespace = RegExp.$2
    130             }else{
    131                 this.type = type;
    132                 this.namespace = '';
    133             }
    134         }
    135 
    136         Event.prototype = {
    137             preventDefault: function(){
    138                 var e = this.originalEvent;
    139                 if(e.preventDefalut){
    140                     return e.preventDefault();
    141                 }
    142                 e.returnValue = false;
    143                 this.isPreventDefault = returnTrue;
    144                 return;
    145             },
    146             stopPropagation: function(){
    147                 var e = this.originalEvent;
    148                 if(e.stopPropagation){
    149                     return e.stopPropagation();
    150                 }
    151                 e.stopBubble = true;
    152                 this.isStopPropagation = returnTrue;
    153                 return;
    154             },
    155             stopImmediatePropagation: function(){
    156                 this.stopPropagation();
    157                 this.isImmediatePropagationStopped = returnTrue;
    158             }
    159         };
    160         //事件修复
    161         function fixEvent(event){
    162             var i, prop, props = [], originalEvent = event.originalEvent;
    163 
    164             props = props.concat('altKey bubbles button cancelable charCode clientX clientY ctrlKey currentTarget'.split(' '));
    165             props = props.concat('data detail eventPhase fromElement handler keyCode layerX layerY metaKey'.split(' '));
    166             props = props.concat('newValue offsetX offsetY originalTarget pageX pageY prevValue relatedTarget'.split(' '));
    167             props = props.concat('screenX screenY shiftKey target toElement view wheelDelta which'.split(' '));
    168             for(i=props.length;--i;){
    169                 event[props[i]] = originalEvent[props[i]];
    170             }
    171 
    172             if(!event.target){
    173                 event.target = event.srcElement;
    174             }
    175 
    176             if(event.target.nodeType == 3){
    177                 event.target = event.target.parentNode;
    178             }
    179 
    180             if(!event.relatedTarget){
    181                 event.relatedTarget = event.fromElement === event.target? event.toElement : event.fromElement;
    182             }
    183 
    184             if(!event.which && (event.charCode || event.keyCode)){
    185                 event.which = event.charCode ? event.charCode : event.keyCode ? event.keyCode : null;
    186             }
    187 
    188             if(!event.pageX || !event.pageY){
    189                 event.pageX = event.clientX + (doc.documentElement && doc.documentElement.scrollLeft || doc.body && doc.body.scrollLeft || 0)
    190                 - (doc.documentElement && doc.documentElement.clientLeft || doc.body && doc.body.clientLeft || 0);
    191                 event.pageY = event.clientY + (doc.documentElement && doc.documentElement.scrollTop || doc.body && doc.body.scrollTop || 0)
    192                     - (doc.documentElement && doc.documentElement.clientTop || doc.body && doc.body.clientTop || 0);
    193             }
    194 
    195             if(!event.which && event.button != undefined){ //ie下 0 无动作, 1 左键 ,2 右键, 4 中间键
    196                 event.which = (event.button & 1) ? 1 : (event.button & 2) ? 3 : (event.button & 4) ? 2 : 0;
    197             }
    198             return event;
    199         }
    200 
    201         function bind(el,type,fn){
    202             if(el.nodeType && el.nodeType == 3 || el.nodeType == 8) return;
    203 
    204             var elData= S._data(el),events,handlers,typeEvents;
    205             if(!elData) {
    206                 S._lockData(el);  //开辟缓存
    207                 elData = S._data(el);
    208             }
    209             if(!elData['events']){
    210                 elData['events']  = {};
    211             }
    212             events = elData['events'];
    213             handlers = elData['handlers'];  // 目前先不对其赋值
    214             if(!events[type]){
    215                 events[type] = [];
    216             }
    217             typeEvents = events[type];
    218 
    219             var handlerObj;
    220             if(S.isFunction(fn)){
    221                 handlerObj = new Handler({handler: fn});
    222             }else if(S.isObject(fn)){
    223                 handlerObj = new Handler(fn);
    224             }else{
    225                 return;
    226             }
    227 
    228             handlerObj.handlerHook = function(event,args){  // 函数钩子,用于unbind删除回调函数
    229                 event = event || window.event;
    230                 var e = new Event(event);
    231                 e = fixEvent(e);
    232                 execHandlers(el,e,args,el);
    233             };
    234 
    235 
    236             if(!typeEvents || !typeEvents.length)
    237                 addEvent(el,type,handlerObj.handlerHook);
    238 
    239             typeEvents.push(handlerObj);
    240         }
    241 
    242         function unbind(el,type,fn){
    243             var newEvents = [];
    244             if(el.nodeType && el.nodeType == 3 || el.nodeType == 8) return;
    245 
    246             var elData= S._data(el),events,handlers,typeEvents;
    247 
    248             if(!elData || !elData['events'])return;
    249 
    250             if(arguments.length == 1){  // 删除该元素所有缓存 事件
    251                 for(var i in elData['events']){
    252                     if(elData['events'].hasOwnProperty(i)){
    253                         for(var j=0,len=elData['events'][i].length;j<len;j++){
    254                             removeEvent(el,i,elData['events'][i][j].handlerHook);
    255                         }
    256                     }
    257                 }
    258                 S._unData(el);
    259             }
    260 
    261             events = elData['events'][type];
    262             newEvents = events.concat();
    263 
    264             if(arguments.length == 2 && events){
    265                 try{
    266                     for(var i= 0,len=events.length;i<len;i++){
    267                         removeEvent(el,type,events[i].handlerHook);
    268                     }
    269                 }catch(e){
    270                     throw new TypeError('哎呀啊,解除回调出现意外')
    271                 }
    272 
    273                 events = {};
    274                 delete elData[type];
    275             }
    276 
    277             if(arguments.length == 3){
    278                 for(var i= 0,len=events.length;i<len;i++){
    279                     if(events[i].handler === fn){
    280                         try{
    281                             removeEvent(el,type,events[i].handlerHook);
    282                         }catch(e){
    283                             throw new TypeError('哎呀啊,解除回调出现意外')
    284                         }
    285                         newEvents.splice(i,1);
    286                     }
    287                 }
    288             }
    289             elData['events'][type] = events = newEvents;
    290         }
    291 
    292         function trigger(el,type,args){
    293             if(el.nodeType && el.nodeType == 3 || el.nodeType == 8) return;
    294 
    295             var elData= S._data(el),events,handlers,typeEvents;
    296 
    297             if(!elData || !elData['events'] || !elData['events'][type])return;
    298             events = elData['events'][type];
    299 
    300             var handlerObj,event;
    301             event = {
    302                 target: el,
    303                 type: type,
    304                 data: args
    305             };
    306             for(var len=events.length;--len>=0;){
    307                 handlerObj = events[len];
    308                 handlerObj.handlerHook(event,args);
    309             }
    310         }
  • 相关阅读:
    【设计模式】适配器模式
    【设计模式】原型模式
    【设计模式】建造者模式
    【设计模式】抽象工厂模式
    我的稳定的SpingBoot项目依赖
    Vue中使用iconfont
    Vue-兄弟组件传值
    CSS让高度百分百的方案
    css修改overflow滚动条默认样式
    CSS去除input和textarea点击选中框
  • 原文地址:https://www.cnblogs.com/accordion/p/4092425.html
Copyright © 2011-2022 走看看