jQuery之事件綁定到觸發全過程及知識點補充

前言:
最重要的仍是最後的流程圖,能夠試着根據流程圖手寫實現$().on(),下篇文章會放出模擬實現的代碼。緩存

1、舉例app

<div id="A" style="background-color: deeppink">
  這是A

    <div id="C" style="background-color: aqua">
    這是C
    </div>

</div>

  $("#A").on("click" ,function (event) {
    console.log(event,"A被點擊了")
  })

  $("#A").on("click" ,"#C",function (event) {
    console.log(event,"點擊了C,即C委託A的click事件被點擊了")
  })

2、$().on()
(1)進行參數的調整
(2)調用jQuery.event.add()方法函數

3、jQuery.event.add()最終調用elem.addEventListener()來綁定事件
注意:
(1)綁定經常使用的事件(如:click、focus),使用handleObj保存ui

handleObj = jQuery.extend( {
          //click,mouseout...
          type: type,
          //click,mouseout...
          origType: origType,
          data: data,
          //事件處理函數,如 function(){console.log('aaaa')}
          handler: handler,
          //索引,用於關聯元素和事件
          guid: handler.guid,
          //事件委託的標誌,也是委託的對象選擇器
          selector: selector,
          needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
          //命名空間,同一click事件有兩個事件處理程序handler的話,
          //用這個標識,方便刪除或添加handler
          namespace: namespaces.join( "." )
        }, handleObjIn );

(2)若是綁定的是自定義事件(如:windowResize),則使用handleObjIn保存this

if ( handler.handler ) {
   handleObjIn = handler;
   handler = handleObjIn.handler;
   selector = handleObjIn.selector;
}

(1)、(2)都會初始化事件處理器(addEventListener):spa

//第一次綁定事件,走這裏
        // 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 ) {
            //目標元素有addEventListener的話,調用綁定click事件
            //eventHandle就綁定到addEventListener上
            if ( elem.addEventListener ) {
              elem.addEventListener( type, eventHandle );
            }
          }
        }

4、jQuery的事件綁定爲什麼不直接綁定在目標元素身上,而是元素和事件分離?
打印$("#A")設計

console.log($("#A"),'aaaaaa46')

不要在乎jQueryId不一樣的問題,每次刷新網頁它都會變化code

能夠看到對象

jQuery的事件和觸發事件的handler是分離的,
事件集合 存在 事件緩存dataPrivevents上,索引

//獲取數據緩存
elemData = dataPriv.get( elem );

而handler是由jQuery.event.dispatch()處理

elemData.handle = function( e ) {
  jQuery.event.dispatch.apply( elem, arguments )
}

爲何要分離?由於元素若是綁定click事件一百次,很耗內存。因此須要將這一百個同類型的事件保存到一個click事件集合中,而後在這一大個click事件集合內,根據guid來執行某一次的click處理代碼

同一事件的處理:

$('body').on('click', '#one', function(e) {
  show('委託到one觸發')
})
$('body').on('click', '#two', function(e) {
  show('委託到two觸發')
})

events是jQuery內部的事件隊列
handle是真正綁定到element上的事件處理函數

body:{
  events:{
    click:[
      0:{
        guid: 1,
        data: undefined,
        namespace: "",
        origType: "click",
        //事件委託的標誌
        selector: "#one",
        type: "click",
        handler: function(){xxx},
        }
      1:{
        guid: 2,
        data: undefined,
        namespace: "",
        origType: "click",
        //事件委託的標誌
        selector: "#two",
        type: "click",
        handler: function(){xxx},
        }
    ]
  },
  handle: function(){
    jQuery.event.dispatch.apply( elem, arguments )
  }
}

能夠看到,針對同一類型的事件(如click),重複綁定不會再建立新的內存(new Object會有新內存),而是在events裏添加新的綁定事件。

記得看第十一點!

5、guid的做用?
添加guid的目的是由於handler沒有直接跟元素節點發生關聯,因此須要一個索引來尋找或者刪除handler

6、命名空間namespace的做用?

$("#one").on("click.one",function () {
   console.log("one被點擊了")
 })
 $("#one").on("click.two",function () {
   console.log("two被點擊了")
 })

命名空間爲:

#one:{
  events:{
    click:[
      0:{
        namespace: "one",
        handler: function(){console.log("one被點擊了")},
        }
      1:{
        namespace: "two",
        handler: function(){xxx},
        }
    ]
  },
}

利用命名空間刪除事件:

$("#one").off("click.one")

7、jQuery.event.special 的處理機制
綁定的事件,有些是不能統一處理的,好比load事件,是不支持冒泡的,因此即便開發者未用event.stopPropagation,jQuery也要阻止其冒泡:

jQuery.event ={
  special: {
    load: {
        // Prevent triggered image.load events from bubbling to window.load
        //阻止冒泡
        noBubble: true
    },
    focus: {
        // Fire native event if possible so blur/focus sequence is correct
        trigger: function() { },
        delegateType: "focusin"
    },
  }
}

8、外部是Event,內部是數據緩存events,二者是不同的
外部Event:

$().on("click","#B",function(event){
  console.log("A被點擊了")
})

//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}
}

內部緩存events:

let events = dataPriv.get( this, "events" )

events[
    delegantCount:1,
   {
      data:undefined,
      guid: 2,
      handler:function(){console.log("B委託A被點擊了")},
      namespace: "clickA",
      origType: "click",
      selector: "#B",
      type: "click.clickA",
    },
   {
     data:undefined,
      guid: 1,
      handler:function(){console.log("A被點擊了")},
      namespace: "",
      origType: "click",
      selector: undefined,
      type: "click",
    }   


]

9、爲何要使用fix()來重構 js 的原生 MouseEvent 對象呢?
(1)jQuery 有本身的一套event處理機制,因此須要符合jQueryevent對象
(2)能夠傳遞 data 數據,即用戶自定義的數據。

10、trigger的觸發機制

$("#A").on("click" ,function (event) {
    console.log(event,"A被點擊了")
  })

元素#A自己綁定了一個click事件,可是click是原生事件,它是靠 addEventListener綁定來觸發事件的。

可是!jQuerytrigger是可以無差異模擬這個交互行爲的

$("#A").trigger("click")

trigger()的功能上就能夠解釋 爲何jQuery要設計元素與數據分離了:

若是是直接綁定的話就沒法經過trigger的機制去觸發click事件,
正是由於jQuery沒有直接把事件相關的handler與元素直接綁定,而是採用了分離處理,
因此咱們經過trigger觸發click事件與addEventListener觸發click事件的處理流程是一致的,不一樣的只是觸發的方式而已。

可是,通trigger觸發的事件是沒有事件對象(event)、冒泡(bubble)這些特性的,因此咱們須要有一個功能 能模擬出事件對象,而後生成一個遍歷樹(eventPath)模擬出冒泡行爲,這個就交給了trigger方法了

關於$().trigger()的源碼解析請看:jQuery源碼解析之trigger()


最後,附上本身作的 jQuery事件綁定到觸發全過程的流程圖:


(完)

相關文章
相關標籤/搜索