這篇筆記很大的幫助來源於慕課網上jQuery源碼解讀課程,雖然jQuery如今已經慢慢衰退了,可是做爲一個學習者,我本身仍是很願意去琢磨琢磨其中的原理。若是有錯誤歡迎指出,謝謝了^0^。node
你們確定都熟悉JavaScript中的瀏覽器事件模型——事件冒泡和事件捕獲。在DOM2級事件處理程序中,咱們可使用addEventListener
方法來添加事件監聽,第三個參數能夠指定是捕獲階段(true)仍是冒泡階段(false),在IE中對應的方法是attachEvent
,且只支持冒泡事件。數組
Q:爲何咱們要使用addEventListener?
咱們以往的寫法是XX.onclick=function(){}
,這樣不能綁定多個相同事件(會覆蓋),也不能選擇是在冒泡階段/捕獲階段。瀏覽器
Q:這樣的綁定可能會出現什麼問題?app
Q:什麼是事件委託?
注意看上面問題的第3點,假設咱們的頁面上有100個p標籤,那咱們就要添加100個事件監聽,這樣會影響頁面的運行性能。
可是若是咱們給body一個click監聽,當用戶點擊的頁面中的p標籤,實質上也是點擊body,因此body的這個click事件會從target
開始進行層層冒泡,若是target就是咱們的目標元素,那麼就相應去運行咱們想調用的函數,這樣功效不也等同於監聽了p標籤嗎?
因此事件委託的定義是:函數
事件目標自身不處理事件,而是把處理任務委託給其父元素或者祖先元素,甚至根元素(document)
這樣一來,就算目標目前不存在它的容器裏也不會發生錯誤,也一樣減小了綁定次數。
因此模擬給a加上事件委託代碼以下:性能
(function() { var parent = document.getElementById('parent'); //給祖先元素綁定監聽 parent.addEventListener('click', handler, false); function handler(e) { //或許觸發冒泡的元素,若是元素是a,就作想作的事情 var x = e.target; if (x.nodeName.toLowerCase() === 'a') { alert('a被點擊了!'); e.preventDefault(); } }; })();
在jQuery中,與事件綁定相關的方法有如下幾種:學習
Tips:live()在1.9版本已被廢除。
而以上的幾種方法,bind() ,live() ,delegate()都是調用了on()來實現的,而歸根結底都是使用了addEventListener
以及事件冒泡來實現的。ui
調用:this
$("#test").bind("click",function(){ console.log(1); });
源碼spa
bind()方法實際直接調用了on()方法,types是事件類型,data是傳入的要執行函數,更詳細的咱們一塊兒放到on()方法去看。可是先說清楚,它沒有用委託機制,元素必需要存在。
調用:
$("#test").bind("click",function(){ console.log(1); });
源碼:
live()方法使用了事件委託機制,把全部的委託都交給了this.context(至關於document
)來完成。
調用:
$("body").delegate("#test","click",function(){ console.log(1); })
源碼:
這裏看到其實delegate和live是相似的,只是live是this.context調用on方法,delegate是本身能夠指定委託對象,因此直接this調用on方法
調用:
//直接調用 $("#test").on("click",function(){ console.log(1); }) //委託調用 $("body").on("click","#test",function(){ console.log(1); })
源碼:
值得一提,one方法也是調用了on方法,不過它是隻調用一次事件,隨後就解綁。
/* on()方法,參數可能會錯位,因此須要處理 * elem: 被委託元素(調用on方法選擇元素) * types:事件類型(咱們舉例子傳入的都是click) * selector:可選,真正的目標元素 * data: 可選,額外數據 * fn: 可選,監聽到後要調用的函數 * one:內部傳入,是不是one()方法調用的 */ function on( elem, types, selector, data, fn, one ) { var origFn, type; //傳入的types多是一個數組(能夠同時綁定多個事件) if ( typeof types === "object" ) { //若是selector不是string,說明傳入了types,data if ( typeof selector !== "string" ) { //因此這修正一下data和selector的值 data = data || selector; selector = undefined; } //遍歷每個事件,去分別添加綁定 for ( type in types ) { on( elem, type, selector, data, types[ type ], one ); } //返回elem,以支持鏈式調用 return elem; } //若是隻傳了三個參數,說明是傳入了types,fn if ( data == null && fn == null ) { // 把參數歸位 fn = selector; data = selector = undefined; } else if ( fn == null ) { // 若是隻有fn是null,且selector是string,說明是傳入了types,fn,selector if ( typeof selector === "string" ) { //參數歸位 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 ) { //保存要調用的函數 origFn = fn; fn = function( event ) { //解綁事件 jQuery().off( event ); //調用函數 return origFn.apply( this, arguments ); }; //給origFn和fn使用一樣的記錄函數的guid,方便移除 fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } //給每一個元素註冊事件處理程序 return elem.each( function() { jQuery.event.add( this, types, fn, data, selector ); } ); }
這裏的guid和event.add()就涉及到了jQuery的事件機制,比較複雜,因此另再開坑來學習。