前言:
最重要的仍是最後的流程圖,能夠試着根據流程圖手寫實現$().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是分離的,
事件集合 存在 事件緩存dataPriv
的events
上,索引
//獲取數據緩存 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
處理機制,因此須要符合jQuery
的event
對象
(2)能夠傳遞 data 數據,即用戶自定義的數據。
10、trigger的觸發機制
$("#A").on("click" ,function (event) { console.log(event,"A被點擊了") })
元素#A
自己綁定了一個click
事件,可是click
是原生事件,它是靠 addEventListener
綁定來觸發事件的。
可是!jQuery
的trigger
是可以無差異模擬這個交互行爲的
$("#A").trigger("click")
從trigger()
的功能上就能夠解釋 爲何jQuery
要設計元素與數據分離了:
若是是直接綁定的話就沒法經過trigger
的機制去觸發click
事件,
正是由於jQuery
沒有直接把事件相關的handler
與元素直接綁定,而是採用了分離處理,
因此咱們經過trigger
觸發click
事件與addEventListener
觸發click
事件的處理流程是一致的,不一樣的只是觸發的方式而已。
可是,通trigger
觸發的事件是沒有事件對象(event)、冒泡(bubble)這些特性的,因此咱們須要有一個功能 能模擬出事件對象,而後生成一個遍歷樹(eventPath)模擬出冒泡行爲,這個就交給了trigger
方法了
關於$().trigger()
的源碼解析請看:jQuery源碼解析之trigger()
最後,附上本身作的 jQuery
事件綁定到觸發全過程的流程圖:
(完)