zoukankan      html  css  js  c++  java
  • jQuery事件绑定原理 简单解析与实现....

    下面的内容只是自己的一些理解,水平有限,难免有错,望指正...
     
     
    jq里面有一个data的方法,给dom元素绑定相关的数据的。当给dom用jq的方法绑定了事件,会生成对应的时间列表
    可以看下面的例子(请在firefox中查看 因为firefox中对象支持toSource())
     
    View Code
     1 <!DOCTYPE html>
     2 <html>
     3 <head>
     4 <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
     5 <title></title>
     6 </head>
     7 <body>
     8 <div id="test"></div>
     9 <script type="text/javascript" src="http://common.cnblogs.com/script/jquery.js"></script>
    10 <script type="text/javascript">
    11     window.onload = function(){
    12         alert($.data($('#test')[0],'events'));//null
    13         alert($.data($('#test')[0],'handle'));//null
    14         $('#test')
    15         .bind('click',function(){
    16             alert(1)
    17         })
    18         .bind('mouseover',function(){
    19             alert(2)
    20         })
    21         .bind('click',function(){
    22             alert(3)
    23         })
    24         .bind('click',function(){
    25             alert(4)
    26         })
    27         alert($.data($('#test')[0],'events').toSource());//时间列表
    28         alert($.data($('#test')[0],'handle').toSource());//执行的函数
    29 
    30     }
    31 </script>
    32 </body>
    33 </html>
     
     
    data是给元素绑定数据的
    数据源是 cache对象
    当元素绑定数据的时候 会给元素添加一个属性   jQueryxxx      xxx为执行jq的时间戳
    这里要说明一下,有一个uuid 他是累加的
    jQueryxxx的值就是这个uuid
    cache 的 key就是这个 uuid
    value就是要存的数据
    data对于事件的绑定是很重要的................................
    View Code
     1 
     2     function now(){  
     3         return +new Date;  
     4     }; 
     5     var win     = this,
     6         expando = "jQuery" + now(),  
     7            uuid    = 0,   
     8           cache   = {};
     9     win.data = function(elem, name, data){  
    10         var id = elem[expando];  
    11         if(!id)  
    12             id = elem[expando] = ++uuid;  
    13         if(name&&!cache[id])  
    14             cache[id] = {};  
    15         if(data !== undefined)  
    16             cache[id][name] = data;  
    17         return name  
    18             ? cache[id][name]  
    19             : id;  
    20     }
    21     win.removeData = function(elem, name){  
    22         var id = elem[expando];  
    23         if (name){  
    24             if (cache[id]) {  
    25                 delete cache[id][name];  
    26                 name = "";  
    27                 for ( name in cache[ id ] )  
    28                     break;  
    29     
    30                 if ( !name )  
    31                     removeData(elem);  
    32             }   
    33         }else{    
    34                 try {  
    35                     delete elem[expando];  
    36                 } catch(e){  
    37                     if ( elem.removeAttribute )  
    38                         elem.removeAttribute( expando );  
    39                 }  
    40                 delete cache[id];  
    41         }  
    42     }
    43     
    44 
    45     win.each = function( object, callback, args ) {  
    46         var name, i = 0, length = object.length;  
    47 
    48         if ( args ) {  
    49 
    50             if ( length === undefined ) {  
    51                 for ( name in object )  
    52                     if ( callback.apply( object[ name ], args ) === false )  
    53                         break;  
    54             } else  
    55 
    56                 for ( ; i < length; )  
    57                     if ( callback.apply( object[ i++ ], args ) === false )  
    58                         break;  
    59         } else {  
    60 
    61             if ( length === undefined ) {  
    62                 for ( name in object )  
    63                     if ( callback.call( object[ name ], name, object[ name ] ) === false )  
    64                         break;  
    65             } else  
    66                 for ( var value = object[0];  
    67                     i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}  
    68         }  
    69   
    70         return object;  
    71     }
     
    接着实现添加事件
    jq里面是在 jQuery.event里面的add方法
    在add方法里面实现了一下一些功能
    取元素的events,handle这2个data绑定的数据
    events存放的是事件列表
    格式如下
    {
    click: [{handler:function(){},type:"click",guid:'xx'}.......],
    mouse:[......]
    }
    handle是执行的函数
    (所有的执行函数都是一样的 他们遍历事件列表    执行对应的事件) 
    然后遍历types   因为可以绑定多个事件
    回调函数也会给几个属性
    假设回调函数是handler
    handler.guid = gevent.guid++
    handler.type =  name
    name应该算一个特殊的命名  方便删除用的
    比如
    $('#xx')
    .bind('click',function(){})
    .bind('click.d',handler)
    name就是d了
    删除的时候可以只删除d那个事件  不删除上面的那个 click事件
     
    最后是给元素绑定事件 但是执行的函数都是
    function(){
     gevent.handle.apply(arguments.callee.elem, arguments);
    });
     
    View Code
     1     win.gevent = {
     2         guid : 1,
     3         add  : function (elem, types, handler){
     4             if ( elem.nodeType == 3 || elem.nodeType == 8 )
     5                 return;
     6 
     7             if ( elem.setInterval && elem != window )
     8                 elem = window;
     9             
    10             //给函数一个唯一标识的索引  方便后面删除该事件    
    11             if ( !handler.guid )
    12                 handler.guid = this.guid++;
    13             
    14             //获得该元素的events handle 下的数据    
    15             var events = data(elem, "events"|| data(elem, "events", {}),
    16                 handle =data(elem, "handle"|| data(elem, "handle"function(){
    17                     //gevent.handle才是各种行为触发后会执行的函数
    18                     gevent.handle.apply(arguments.callee.elem, arguments);
    19                 });
    20                 
    21             handle.elem = elem;
    22 
    23             //遍历事件名 因为可以是 click mouseover 
    24             each(types.split(/\s+/), function(index, type) {
    25                 var namespaces = type.split(".");
    26                 //获得事件名
    27                 type = namespaces.shift();
    28                 //去掉点后面的东西 是个特殊的命名  在删除的时候可以指定删除他  如 click.d
    29                 //用事件的type 记录住这个特殊的命名
    30                 handler.type = namespaces.slice().sort().join(".");
    31                 
    32                 //获得该事件是否已经存在events 这个对象里面了
    33                 var handlers = events[type];
    34                 
    35                 //如果不存在该事件 给元素绑定该事件                
    36                 if (!handlers) {
    37                     handlers = events[type] = {};
    38                     
    39                     if (elem.addEventListener)
    40                         elem.addEventListener(type, handle, false);
    41                     else if (elem.attachEvent)
    42                         elem.attachEvent("on" + type, handle);                                        
    43                 }
    44                 
    45                 //吧函数放到元素的该事件的列表里面
    46                 handlers[handler.guid] = handler;                                        
    47             });
    48             elem = null;                                                    
    49         }
    50 }
     
    gevent.hander是绑定事件真正执行的函数
    在gevent.hander里面也有取.特殊命名的地方  但是不知道做什么用的
    hander里面先对event进行包装
    包装见gevent. fix 和 setEvent
    主要是对做一个原生event的一个copy  然后把不兼容的方法  都合成兼容的写法
    然后取元素的events (事件列表)
    然后遍历这个事件列表  判断type是不是事件列表的key 是的话就执行事件
    在执行列表函数的时候会判断返回值
    如果返回false  还可以组织事件冒泡 和 默认行为
     
     
    View Code
      1     win.gevent = {
      2         handle : function(event){
      3             var all, handlers;
      4             //包装event
      5             event = arguments[0= gevent.fix( event || window.event );
      6 
      7             event.currentTarget = this;
      8             
      9             //这里的........
     10             var namespaces = event.type.split(".");
     11             event.type = namespaces.shift();
     12             all = !namespaces.length;
     13             
     14             var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\."+ "(\\.|$)");
     15             
     16             //取这个元素的该行为 的 事件列表
     17             handlers = (data(this"events"|| {} )[event.type];            
     18             
     19             //遍历这个事件列表 执行该执行的东西
     20             for ( var j in handlers ) {
     21                 var handler = handlers[j];
     22                 if ( all || namespace.test(handler.type) ) {
     23                     
     24                     // Pass in a reference to the handler function itself
     25                     // So that we can later remove it
     26                     // jq上的注释是是这么写的 把event的handler 引用这个事件 方便之后移除
     27                     // 但是在remove里面 并没有用到event的handler  不知道这里到底有什么用  且有多个事件的时候这个事件被取代
     28                     event.handler = handler;
     29                     
     30                     //执行事件 并且是用元素调用的事件 可以吧事件里面的this执行元素 ret为函数的返回值
     31                     var ret = handler.apply(this, arguments);
     32                     
     33                     //如果有返回值  且返回值是false 执行阻止事件冒泡 阻止执行事件默认行为                        
     34                     if( ret !== undefined ){
     35                         event.result = ret;
     36                         if ( ret === false ) {
     37                             event.preventDefault();
     38                             event.stopPropagation();
     39                         }
     40                     }    
     41                 }                
     42             }
     43     
     44         },
     45         props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),        
     46         fix : function(event){
     47             //new setEvent会给event给以个expando属性 如果有中个属性 说明已经生成了event了 不需要在次对event进行包装
     48             if ( event[expando] )
     49                 return event;
     50                 
     51             //保留一个原始的event
     52             // new一个新的event 这个与原始的event是不同的
     53             var originalEvent = event;
     54             event = new setEvent( originalEvent );
     55             
     56             //获得原始event的属性值  有哪些属性值 见 this.props
     57             for ( var i = this.props.length, prop; i; ){
     58                 prop = this.props[ --i ];
     59                 event[ prop ] = originalEvent[ prop ];
     60             }
     61             
     62             //将目标元素同一成event.target
     63             if ( !event.target )
     64                 event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
     65             
     66             //如果发现是文本节点 取他的父节点
     67             if ( event.target.nodeType == 3 )
     68                 event.target = event.target.parentNode;
     69     
     70             
     71             if ( !event.relatedTarget && event.fromElement )
     72                 event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;                            
     73             
     74             return event;
     75         }        
     76     }
     77     win.setEvent = function(src){
     78         // Allow instantiation without the 'new' keyword
     79         
     80         // Event object
     81         if( src && src.type ){
     82             this.originalEvent = src;
     83             this.type = src.type;
     84         // Event type
     85         }else
     86             this.type = src;
     87     
     88         // timeStamp is buggy for some events on Firefox(#3843)
     89         // So we won't rely on the native value
     90         this.timeStamp = now();
     91         
     92         // Mark it as fixed
     93         this[expando] = true;
     94 
     95     }
     96     function returnFalse(){
     97         return false;
     98     }
     99     function returnTrue(){
    100         return true;
    101     }
    102     setEvent.prototype = {
    103         preventDefault: function() {    
    104             var e = this.originalEvent;
    105             if!e )
    106                 return;
    107             // if preventDefault exists run it on the original event
    108             if (e.preventDefault)
    109                 e.preventDefault();
    110             // otherwise set the returnValue property of the original event to false (IE)
    111             e.returnValue = false;
    112         },
    113         stopPropagation: function() {    
    114             var e = this.originalEvent;
    115             if!e )
    116                 return;
    117             // if stopPropagation exists run it on the original event
    118             if (e.stopPropagation)
    119                 e.stopPropagation();
    120             // otherwise set the cancelBubble property of the original event to true (IE)
    121             e.cancelBubble = true;
    122         },
    123         stopImmediatePropagation:function(){
    124             this.isImmediatePropagationStopped = returnTrue;
    125             this.stopPropagation();
    126         },
    127         isImmediatePropagationStopped: returnFalse
    128     };    

    一个完整的例子

  • 相关阅读:
    C# 中 finally 的用法
    相似度算法(转载)
    OpenCV 安装与调试
    win7 debug 工具
    wpf 登录时显示状态动态图
    C# 比较两张图片是否完全相同
    ABSD 基于架构的软件设计方法方法简介(摘抄)
    基于SQL Server的简单数据同步方案
    软件:产品和过程的统一(转)
    cpp extern 关键字用法
  • 原文地址:https://www.cnblogs.com/wtcsy/p/2169079.html
Copyright © 2011-2022 走看看