博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jQuery源码解析之jQuery.event.dispatch()
阅读量:6163 次
发布时间:2019-06-21

本文共 11956 字,大约阅读时间需要 39 分钟。

一、起源

jQuery.event.add()方法最终是用addEventListener绑定事件的:

elem.addEventListener( type, eventHandle ) 复制代码

eventHandle方法正是等于jQuery.event.dispatch()

  if ( !( eventHandle = elemData.handle ) ) {
        eventHandle = elemData.handle = function( e ) {
          return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?             jQuery.event.dispatch.apply( elem, arguments ) : undefined;         };    } 复制代码

二、$.event.dispatch()

作用:
触发绑定的事件的处理程序

源码:

    //源码5472行     //nativeEvent即原生MouseEvent     //触发事件的处理程序     dispatch: function( nativeEvent ) {
      //修正event对象       // Make a writable jQuery.Event from the native event object       var event = jQuery.event.fix( nativeEvent );       console.log(event,'event5479')       var i, j, ret, matched, handleObj, handlerQueue,         args = new Array( arguments.length ),         //获取click事件的处理程序集合,结构如下:         //[         // {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 1},         // {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 2},         // delegateCount:0,         //]         //从数据缓存中获取事件处理集合         handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],         //click:{
        // trigger:{},         // _default:{}         //}         special = jQuery.event.special[ event.type ] || {};       // Use the fix-ed jQuery.Event rather than the (read-only) native event       args[ 0 ] = event;       for ( i = 1; i < arguments.length; i++ ) {
        args[ i ] = arguments[ i ];       }       //this即目标元素       //delegateTarget:委托目标       event.delegateTarget = this;       //这段代码压根不会执行,因为全局搜索没找到preDispatch       // Call the preDispatch hook for the mapped type, and let it bail if desired       if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
        return;       }       // Determine handlers       //结构如下       //[{
      // elem:xx,       // handlers:[       //  {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 1},       //  {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 2},       //  ]       //}]       //获取handler队列       handlerQueue = jQuery.event.handlers.call( this, event, handlers );       // Run delegates first; they may want to stop propagation beneath us       i = 0;       //没有执行stopPropagation()的话       console.log(handlerQueue,'handlerQueue5525')       //先判断有没有冒泡       //再判断有没有阻止剩下的handler执行       while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
        console.log(matched,'matched5542')         event.currentTarget = matched.elem;         j = 0;         //handleObj即单个事件处理程序         //没有执行stopImmediatePropagation()的话         //依次执行每一个handler         while ( ( handleObj = matched.handlers[ j++ ] ) &&         !event.isImmediatePropagationStopped() ) {
          // Triggered event must either 1) have no namespace, or 2) have namespace(s)           // a subset or equal to those in the bound event (both can have no namespace).           if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {
            //通过循环将为event添加handleObj和handleObj.data             event.handleObj = handleObj;             event.data = handleObj.data;             //关键代码,执行事件处理程序handler             ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||               handleObj.handler ).apply( matched.elem, args );             if ( ret !== undefined ) {
              //event.result赋值ret               if ( ( event.result = ret ) === false ) {
                //阻止默认行为                 event.preventDefault();                 //阻止冒泡                 event.stopPropagation();               }             }           }         }       }       // Call the postDispatch hook for the mapped type       if ( special.postDispatch ) {
        special.postDispatch.call( this, event );       }       console.log(handlers,'event5587')       //undefined       return event.result;     }, 复制代码

解析:

(1)jQuery.event.fix()

作用:
将原生事件对象MouseEvent修正(fix)成jQueryevent对象

源码:

    //源码5700行     fix: function( originalEvent ) {
      //如果存在属性id则原样返回(因为已处理成jQueryEvent)       return originalEvent[ jQuery.expando ] ?         originalEvent :         new jQuery.Event( originalEvent );     }, 复制代码

解析:

可以看到fix的本质是新建一个event对象,再看jQuery.Event()方法

(2)jQuery.Event()

源码:

 //click,false   //修正event对象   //源码5777行   //src即MouseEvent   jQuery.Event = function( src, props ) {
    // Allow instantiation without the 'new' keyword     if ( !( this instanceof jQuery.Event ) ) {
      return new jQuery.Event( src, props );     }     // Event object     //src.type=click     if ( src && src.type ) {
      //MouseEvent       this.originalEvent = src;       //click       this.type = src.type;       // Events bubbling up the document may have been marked as prevented       // by a handler lower down the tree; reflect the correct value.       this.isDefaultPrevented = src.defaultPrevented ||       src.defaultPrevented === undefined &&       // Support: Android <=2.3 only       src.returnValue === false ?         returnTrue :         returnFalse;       // Create target properties       // Support: Safari <=6 - 7 only       // Target should not be a text node (#504, #13143)       this.target = ( src.target && src.target.nodeType === 3 ) ?         src.target.parentNode :         src.target;       this.currentTarget = src.currentTarget;       this.relatedTarget = src.relatedTarget;       // Event type     } else {
      //click       this.type = src;     }     // Put explicitly provided properties onto the event object     //false     if ( props ) {
      jQuery.extend( this, props );     }     // Create a timestamp if incoming event doesn't have one     this.timeStamp = src && src.timeStamp || Date.now();     // Mark it as fixed     //修正的标志     this[ jQuery.expando ] = true;   }; 复制代码

解析:

简单来说,就是把原生event事件上的常用属性赋值到了jQueryevent

  $("#A").on("click" ,function (event) {
    //这个就是jQuery.Event()构建出的event     console.log(event,"A被点击了")   }) 复制代码

jQueryevent结构如下:

//click的event就是jQuery.Event jQuery.Event{
  handleObj{
    data:undefined,     guid: 2,     handler:function(){console.log("A被点击了")},     namespace: "clickA",     origType: "click",     selector: "#B",     type: "click.clickA",   },   originalEvent:{
    //就是MouseEvent   },   target:div#B,   type: "click",   delegateTarget: div#A,   //fix 的标志   jQuery331087940272164138: true,   currentTarget: div#A,   isDefaultPrevented:xxx,   timeStamp:Date.now(),   isDefaultPrevented:function(){
return false} } 复制代码

注意下originalEventjQuery.extend( this, props )

前者就是原生MouseEvent,只是将原生event作为jQuery.event的originalEvent属性了;
后者是扩展属性,如果开发者想额外加入自定义属性的话。

(3)dataPriv.get( this, "events" )

注意:
jQuery的数据缓存里的events和上面说的event是不同的

数据缓存的events是用来结构如下:

{
  click:[     {
      type: "click",        origType: "click",        data: undefined,        handler: function(){console.log("B委托A绑定click事件")},        guid: 1,       namespace: "",       needsContext: undefined,       selector: #B,     },     {
      type: "click",        origType: "click",        data: undefined,        handler: function(){console.log("A绑定click事件")},        guid: 2,       namespace: "",       needsContext: undefined,       selector: undefined,     },     //事件委托的数量     delegateCount:1,   ],   focus:[     {
      type: "focus",        origType: "focus",        data: undefined,        handler: function(){console.log("A绑定focus事件")},        guid: 3,       namespace: "",       needsContext: undefined,       selector: undefined,     },      delegateCount:0,   ], } 复制代码

(4)jQuery.event.handlers

作用:
获取handler队列

源码:

jQuery.event = {
    //源码5547行     //组装事件处理队列       //event是fix过的MouseEvent, handlers       handlers: function( event, handlers ) {
      var i, handleObj, sel, matchedHandlers, matchedSelectors,         handlerQueue = [],         //0         delegateCount = handlers.delegateCount,         //目标元素         cur = event.target;       //handlers,第一个handler是委托事件,第二个handler是自身事件       // Find delegate handlers       if ( delegateCount &&         // Support: IE <=9         // Black-hole SVG 
 instance trees (trac-13180)         cur.nodeType &&         // Support: Firefox <=42         // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)         // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click         // Support: IE 11 only         // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)         !( event.type === "click" && event.button >= 1 ) ) {
        //循环,event.target冒泡到cur.parentNode,         //直至绑定的目标元素#A,退出循环         for ( ; cur !== this; cur = cur.parentNode || this ) {
          console.log(cur,'cur5618')           // Don't check non-elements (#13208)           // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)           if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
            matchedHandlers = [];             matchedSelectors = {};             //在每一层,依次将委托的事件push进matchedHandlers             //顺序由下到上             for ( i = 0; i < delegateCount; i++ ) {
              handleObj = handlers[ i ];               //sel就是#C               // Don't conflict with Object.prototype properties (#13203)               sel = handleObj.selector + " ";               if ( matchedSelectors[ sel ] === undefined ) {
                matchedSelectors[ sel ] = handleObj.needsContext ?                   jQuery( sel, this ).index( cur ) > -1 :                   //注意:jQuery.find()和jQuery().find()是不一样的                   jQuery.find( sel, this, null, [ cur ] ).length;               }               if ( matchedSelectors[ sel ] ) {
                matchedHandlers.push( handleObj );               }             }             //然后将该层委托事件的数组放进handlers中             //handlerQueue是所有层委托事件的集合             if ( matchedHandlers.length ) {
              handlerQueue.push( { elem: cur, handlers: matchedHandlers } );             }           }         }       }       // Add the remaining (directly-bound) handlers       //最终冒泡到this元素       cur = this;       //1<2       //将除委托事件的事件(如自身绑定的事件)放入handlerQueue中       if ( delegateCount < handlers.length ) {
        handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );       }       //[{
      // elem:xx,       // handlers:[       //  {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 1},       //  {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 2},       //  ]       //}]       return handlerQueue;     }, } 复制代码

解析:

注意下这个双层循环,目的是把每一层的委托事件的集合pushmatchedHandlers,然后再将matchedHandlers放进handlerQueue队列

在处理完每层的委托事件后,将剩下的自身绑定事件再pushhandlerQueue队列中

也就是说,handlerQueue的结构如下:

[ //委托事件   {
   elem:xx,    handlers:[       {
type: "click", origType: "click", data: undefined, handler: ƒ, guid: 1},       {
type: "click", origType: "click", data: undefined, handler: ƒ, guid: 2},     ]   }, //自身绑定事件   {
   elem:xxx,    handlers:[       {
type: "click", origType: "click", data: undefined, handler: ƒ, guid: 3},       {
type: "click", origType: "click", data: undefined, handler: ƒ, guid: 4},     ]   }, ] 复制代码

(5)回过头再往下看dispatch源码,是两个while循环,举个例子来说明下:

  这是A   
    这是B      $("#A").on("click" ,function (event) {
    console.log(event,"A被点击了")   })   $("#A").on("click" ,"#B",function (event) {
    console.log(event,"点击了B,即B委托A的click事件被点击了")   }) 复制代码

那么会

先循环并执行委托事件,
handler=function (event) {console.log(event,"点击了B,即B委托A的click事件被点击了")}
再循环并执行目标元素自身绑定事件,
handler=function (event) {console.log(event,"A被点击了")}
前提是冒泡不被阻止

最后,执行click事件的事件处理程序的关键代码如下:

handleObj.handler.apply( matched.elem, args ) 复制代码

(完)

转载于:https://juejin.im/post/5d01ab225188254ee433c4b7

你可能感兴趣的文章
多路归并排序之败者树
查看>>
java连接MySql数据库
查看>>
深入python的set和dict
查看>>
DEV实现日期时间效果
查看>>
java注解【转】
查看>>
centos 下安装g++
查看>>
下一步工作分配
查看>>
Response. AppendHeader使用大全及文件下载.net函数使用注意点(转载)
查看>>
jQuery最佳实践
查看>>
centos64i386下apache 403没有权限访问。
查看>>
jquery用法大全
查看>>
PC-BSD 9.2 发布,基于 FreeBSD 9.2
查看>>
css斜线
查看>>
Windows phone 8 学习笔记(3) 通信
查看>>
Revit API找到风管穿过的墙(当前文档和链接文档)
查看>>
Scroll Depth – 衡量页面滚动的 Google 分析插件
查看>>
Windows 8.1 应用再出发 - 视图状态的更新
查看>>
自己制作交叉编译工具链
查看>>
Qt Style Sheet实践(四):行文本编辑框QLineEdit及自动补全
查看>>
[物理学与PDEs]第3章习题1 只有一个非零分量的磁场
查看>>