成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

jQuery源碼解析之click()的事件綁定

figofuture / 1997人閱讀

摘要:階段二目標(biāo)瀏覽器找到監(jiān)聽(tīng)器后,就運(yùn)行該監(jiān)聽(tīng)器階段三冒泡目標(biāo)到祖在事件自下而上到達(dá)目標(biāo)節(jié)點(diǎn)的過(guò)程中,瀏覽器會(huì)檢測(cè)不是針對(duì)該事件的監(jiān)聽(tīng)器用來(lái)捕獲事件,并運(yùn)行非捕獲事件的監(jiān)聽(tīng)器。注意下這種情況,是在里的具體實(shí)現(xiàn),即調(diào)用一次后,就執(zhí)行,卸載事件。

前言:
這篇依舊長(zhǎng),請(qǐng)耐心看下去。

一、事件委托
DOM有個(gè)事件流特性,所以觸發(fā)DOM節(jié)點(diǎn)的時(shí)候,會(huì)經(jīng)歷3個(gè)階段:
(1)階段一:Capturing 事件捕獲(從祖到目標(biāo))
事件自上(document->html->body->xxx)而下到達(dá)目標(biāo)節(jié)點(diǎn)的過(guò)程中,瀏覽器會(huì)檢測(cè) 針對(duì)該事件的 監(jiān)聽(tīng)器(用來(lái)捕獲事件),并運(yùn)行捕獲事件的監(jiān)聽(tīng)器。

(2)階段二:Target 目標(biāo)
瀏覽器找到監(jiān)聽(tīng)器后,就運(yùn)行該監(jiān)聽(tīng)器

(3)階段三:Bubbling 冒泡(目標(biāo)到祖)
事件自下而上(document->html->body->xxx)到達(dá)目標(biāo)節(jié)點(diǎn)的過(guò)程中,瀏覽器會(huì)檢測(cè)不是 針對(duì)該事件的 監(jiān)聽(tīng)器(用來(lái)捕獲事件),并運(yùn)行非捕獲事件的監(jiān)聽(tīng)器

二、$().click()
作用:
為目標(biāo)元素綁定點(diǎn)擊事件

源碼:

  //這種寫(xiě)法還第一次見(jiàn),將所有鼠標(biāo)事件寫(xiě)成字符串再換成數(shù)組
  //再一一綁定到DOM節(jié)點(diǎn)上去
  //源碼10969行
  jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
    "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
    "change select submit keydown keypress keyup contextmenu" ).split( " " ),
    function( i, name ) {
      //事件綁定
      // Handle event binding
      jQuery.fn[ name ] = function( data, fn ) {
        return arguments.length > 0 ?
          //如果有參數(shù)的話,就用jQuery的on綁定
          this.on( name, null, data, fn ) :
          //否則使用trigger
          this.trigger( name );
      };
    } );

解析:
可以看到,jQuery 將所有的鼠標(biāo)事件都一一列舉了出來(lái),并通過(guò)jQuery.fn[ name ] = function( data, fn ) { xxx }

如果有參數(shù),則是綁定事件,調(diào)用 on() 方法;
沒(méi)有參數(shù),則是調(diào)用事件,調(diào)用 trigger() 方法( trigger() 放到下篇講 )

三、$().on()
作用:
在被選元素及子元素上添加一個(gè)或多個(gè)事件處理程序

源碼:

  //綁定事件的方法
  //源碼5812行
  jQuery.fn.extend( {
    //在被選元素及子元素上添加一個(gè)或多個(gè)事件處理程序
    //$().on("click",function()=<{})
    //源碼5817行
    on: function( types, selector, data, fn ) {
      return on( this, types, selector, data, fn );
    },
    //xxx
    //xxx
})

最終調(diào)用的是 jQuery.on() 方法:

  //綁定事件的on方法
  //源碼5143行
  //目標(biāo)元素,類型(click,mouseenter,focusin,xxx),回調(diào)函數(shù)function(){xxx}
  function on( elem, types, selector, data, fn, one ) {
    var origFn, type;
    //這邊可以不看
    // Types can be a map of types/handlers
    if ( typeof types === "object" ) {

      // ( types-Object, selector, data )
      if ( typeof selector !== "string" ) {

        // ( types-Object, data )
        data = data || selector;
        selector = undefined;
      }
      for ( type in types ) {
        on( elem, type, selector, data, types[ type ], one );
      }
      return elem;
    }
    //直接調(diào)用$().on()的話會(huì)走這邊
    if ( data == null && fn == null ) {

      // ( types, fn )
      //fn賦值為selector,即function(){}
      fn = selector;
      //再將selector置為undefined
      //注意這個(gè)寫(xiě)法,連等賦值
      data = selector = undefined;
    }
    //調(diào)用像$().click()的話會(huì)走這邊
    else if ( fn == null ) {
      if ( typeof selector === "string" ) {

        // ( types, selector, fn )
        fn = data;
        data = undefined;
      } else {
        
        // ( types, data, fn )
        fn = data;
        data = selector;
        selector = undefined;
      }
    }

    if ( fn === false ) {

      fn = returnFalse;

    } else if ( !fn ) {

      return elem;

    }
    //one()走這里
    if ( one === 1 ) {
      //將fn賦給origFn后,再定義fn
      origFn = fn;
      fn = function( event ) {
        //將綁定給目標(biāo)元素的事件傳給fn,
        //并通過(guò)$().off()卸載掉
        // Can use an empty set, since event contains the info
        jQuery().off( event );
        //在origFn運(yùn)行一次的基礎(chǔ)上,讓origFn調(diào)用fn方法,arguments即event
        return origFn.apply( this, arguments );
      };
      //讓fn和origFn使用相同的guid,這樣就能移除origFn方法
      // Use same guid so caller can remove using origFn
      fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
    }

    return elem.each( function() {
      //最終調(diào)動(dòng)$.event.add方法
      jQuery.event.add( this, types, fn, data, selector );

    } );
  }

解析:
可以看到,由于將 bind()、live() 和 delegate() 都合并進(jìn) on() 后,on() 里面的情況挺復(fù)雜的, data、selector、fn 相互賦值。

注意下 if ( one === 1 ) 這種情況,是 $().one()on()里的具體實(shí)現(xiàn),即調(diào)用一次on()后,就執(zhí)行jQuery().off( event ),卸載事件。

該方法最終調(diào)用 jQuery.event.add( ) 方法

四、jQuery.event.add( )
作用:
為目標(biāo)元素添加事件

源碼:

 //源碼5235行
  /*
 * Helper functions for managing events -- not part of the public interface.
 * Props to Dean Edwards" addEvent library for many of the ideas.
 */
  jQuery.event = {

    global: {},
    //源碼5241行
    //this, types, fn, data, selector
    add: function( elem, types, handler, data, selector ) {

      var handleObjIn, eventHandle, tmp,
        events, t, handleObj,
        special, handlers, type, namespaces, origType,
        //elemData正是目標(biāo)元素jQuery中的id屬性
        //初始值是{}
        elemData = dataPriv.get( elem );
      // Don"t attach events to noData or text/comment nodes (but allow plain objects)
      if ( !elemData ) {
        return;
      }
      //調(diào)用者可以傳入一個(gè)自定義數(shù)據(jù)對(duì)象來(lái)代替處理程序
      // Caller can pass in an object of custom data in lieu of the handler
      if ( handler.handler ) {
        handleObjIn = handler;
        handler = handleObjIn.handler;
        selector = handleObjIn.selector;
      }
      //確保不正確的選擇器會(huì)拋出異常
      // Ensure that invalid selectors throw exceptions at attach time
      // Evaluate against documentElement in case elem is a non-element node (e.g., document)
      if ( selector ) {
        jQuery.find.matchesSelector( documentElement, selector );
      }

      //確保handler有唯一的id
      // Make sure that the handler has a unique ID, used to find/remove it later
      if ( !handler.guid ) {
        handler.guid = jQuery.guid++;
      }
      //如果事件處理沒(méi)有,則置為空對(duì)象
      // Init the element"s event structure and main handler, if this is the first
      //在這里,就應(yīng)經(jīng)給events賦值了,
      // 注意這種寫(xiě)法:賦值的同時(shí),判斷
      if ( !( events = elemData.events ) ) {
        events = elemData.events = {};
      }

      if ( !( eventHandle = elemData.handle ) ) {
        eventHandle = elemData.handle = function( e ) {

          //當(dāng)在一個(gè)頁(yè)面卸載后調(diào)用事件時(shí),取消jQuery.event.trigger()的第二個(gè)事件
          // Discard the second event of a jQuery.event.trigger() and
          // when an event is called after a page has unloaded

          //jQuery.event.triggered: undefined
          //e.type: click/mouseout
          return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
            //讓elem調(diào)用jQuery.event.dispatch方法,參數(shù)是arguments
            jQuery.event.dispatch.apply( elem, arguments ) : undefined;
        };
      }
      //通過(guò)空格將多個(gè)events分開(kāi),一般為一個(gè),如click
      // Handle multiple events separated by a space
      types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
      t = types.length;
      while ( t-- ) {
        tmp = rtypenamespace.exec( types[ t ] ) || [];
        //click
        type = origType = tmp[ 1 ];
        //""
        namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
        // There *must* be a type, no attaching namespace-only handlers
        if ( !type ) {
          continue;
        }
        //如果event改變了它自己的type,就使用特殊的event handlers
        // If event changes its type, use the special event handlers for the changed type
        special = jQuery.event.special[ type ] || {};
        //如果選擇器已定義,確定一個(gè)特殊event api的type
        //否則使用默認(rèn)type
        // If selector defined, determine special event api type, otherwise given type
        type = ( selector ? special.delegateType : special.bindType ) || type;
        //不明白為什么在上面要先寫(xiě)一遍
        // Update special based on newly reset type
        special = jQuery.event.special[ type ] || {};
        //handleObj會(huì)傳遞給所有的event handlers
        // handleObj is passed to all event handlers
        handleObj = jQuery.extend( {
          type: type,
          origType: origType,
          data: data,
          handler: handler,
          guid: handler.guid,
          selector: selector,
          needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
          namespace: namespaces.join( "." )
        }, handleObjIn );
        
        //第一次綁定事件,走這里
        // Init the event handler queue if we"re the first
        if ( !( handlers = events[ type ] ) ) {
          handlers = events[ type ] = [];
          handlers.delegateCount = 0;

          // Only use addEventListener if the special events handler returns false
          if ( !special.setup ||
            special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
            //目標(biāo)元素有addEventListener的話,調(diào)用綁定click事件
            if ( elem.addEventListener ) {
              elem.addEventListener( type, eventHandle );
            }
          }
        }
        //special的add/handleObj.handler.guidd的初始化處理
        if ( special.add ) {
          special.add.call( elem, handleObj );

          if ( !handleObj.handler.guid ) {
            handleObj.handler.guid = handler.guid;
          }
        }

        // Add to the element"s handler list, delegates in front
        if ( selector ) {
          handlers.splice( handlers.delegateCount++, 0, handleObj );
        } else {
          handlers.push( handleObj );
        }
        //一旦有綁定事件,全局通知
        // Keep track of which events have ever been used, for event optimization
        jQuery.event.global[ type ] = true;
      }

    },

...
...

}

解析:
可以看到,很多的 if 判斷,都是在初始化對(duì)象,最后通過(guò) while 循環(huán),調(diào)用目標(biāo)元素的 addEventListener 事件,也就是說(shuō),click()/on() 的本質(zhì)是 element.addEventListener() 事件,前面一系列的鋪墊,都是在為目標(biāo) jQuery 對(duì)象添加必要的屬性。

注意寫(xiě)法 if ( !( events = elemData.events ) ),在賦值的同時(shí),判斷條件

(1)dataPriv

  //取唯一id
  //源碼4361行
  var dataPriv = new Data();

在 jQuery 對(duì)象中,有唯一id的屬性

$("#one")

elemData = dataPriv.get( elem )

① Data()

  //目標(biāo)元素的jQuery id
  //源碼4209行
  function Data() {
    this.expando = jQuery.expando + Data.uid++;
  }

② jQuery.expando

  jQuery.extend( {
    //相當(dāng)于jQuery為每一個(gè)元素取唯一的id
    ///D/g : 去掉非數(shù)字的字符
    // Unique for each copy of jQuery on the page
    //源碼360行
    expando: "jQuery" + ( version + Math.random() ).replace( /D/g, "" ),
    ...  
    ...
})

③ Math.random()
偽隨機(jī),到小數(shù)點(diǎn)后16位

expando: "jQuery" + ( version + Math.random() ).replace( /D/g, "" ),

可以看到 jQuery 的 id 是由 jQuery + 版本號(hào)+ Math.random() 生成的

關(guān)于 Math.random() 是如何生成偽隨機(jī)數(shù)的請(qǐng)看:https://www.zhihu.com/question/22818104

(2)rtypenamespace

  var
    rkeyEvent = /^key/,
    rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
    //事件類型的命名空間
    //舉例:var arr1 = "click.aaa.bbb".match(rtypenamespace);
    //console.log(arr1);//["click.aaa.bbb", "click", "aaa.bbb", index: 0, input: "click.aaa.bbb"]
    //源碼5131行
    rtypenamespace = /^([^.]*)(?:.(.+)|)/;

綜上,綁定事件的本質(zhì)即調(diào)用element.addEventListener()方法,但 jQuery 有太多的情況需要考慮了。

(完)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/109877.html

相關(guān)文章

  • jQuery源碼解析你并不真事件委托及target和currenttarget區(qū)別

    摘要:源碼源碼行被點(diǎn)擊了點(diǎn)擊了,即委托的事件被點(diǎn)擊了優(yōu)先添加委托,再添加其他即委托在上的事件數(shù)量在下標(biāo)為的位置插入委托事件解析可以看到,是優(yōu)先添加委托事件,再添加自身事件,觸發(fā)事件的時(shí)候也是按這個(gè)順序。 showImg(https://segmentfault.com/img/remote/1460000019419722); 前言:請(qǐng)先回顧下我之前寫(xiě)的一篇文章:JavaScript之事件委...

    khs1994 評(píng)論0 收藏0
  • jQuery源碼解析trigger()

    摘要:一和的作用和區(qū)別觸發(fā)被選元素上的指定事件以及事件的默認(rèn)行為比如表單提交不會(huì)引起事件比如表單提交的默認(rèn)行為觸發(fā)所有匹配元素的指定事件只觸發(fā)第一個(gè)匹配元素的指定事件會(huì)冒泡不會(huì)冒泡二被點(diǎn)擊了作用看一源碼觸發(fā)事件,是自定義事件的額外參數(shù)源碼行解析本 showImg(https://segmentfault.com/img/remote/1460000019375685); 一、$().trig...

    Youngs 評(píng)論0 收藏0
  • jQuery源碼解析jQuery.event.dispatch()

    摘要:一起源方法最終是用綁定事件的而方法正是等于二作用觸發(fā)綁定的事件的處理程序源碼源碼行即原生觸發(fā)事件的處理程序修正對(duì)象獲取事件的處理程序集合,結(jié)構(gòu)如下從數(shù)據(jù)緩存中獲取事件處理集合即目標(biāo)元素委托目標(biāo)這段代碼壓根不會(huì)執(zhí)行,因?yàn)槿炙阉鳑](méi)找到結(jié)構(gòu) showImg(https://segmentfault.com/img/remote/1460000019464031); 一、起源jQuery.e...

    GraphQuery 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<