本課主要來說解jQuery.event.trigger的源碼解讀。html
trigger = function(event, data, elem, onlyHandlers){node
if(elem && (elem.nodeType === 3 || elem.nodeType ===8)){ //觸發的元素節點不能是文本節點和註釋節點數組
return;瀏覽器
}緩存
var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType, type = event.type || event , namespaces =[];app
.....性能
if(type.indexOf(".") >= 0 ){ //若是事件類型帶點號,就表明有命名空間,因此須要分解出命名空間spa
namespaces = type.split(".");代理
type = namespaces.shift();htm
namespaces.sort();
}
if((!elem || jQuery.event.customEvent [ type ]) && !jQuery.event.global[type]){ //若是元素不存在或者事件類型是自定義事件,而且以前歷來沒有綁定過此類型的事件,那麼就直接返回。
return;
}
....
ontype = type.indexOf(":") < 0 ? "on" + type : ""; //若是事件類型沒有:字符,就證實是ontype綁定事件
if(!elem){ //若是沒有指明觸發元素,就把整個緩存系統找一遍
cache = jQuery.cache;
for(i in cache){
if(cache[i].events && cache[i].events[ type ]){ //從緩存系統中,找相對應的events屬性,並找到要觸發事件類型的events[type]
jQuery.event.trigger(event,data,cache[i].handle.elem,true); //觸發全部綁定了此事件類型的回調方法。
}
}
return; //直接返回
}
event.result = undefined;//清掉event的result屬性,方便重複使用。
if(!event.target){
event.target = elem;
}
data = data!=null ? jQuery.makeArray(data) :[]; //若是傳入了參數,就把參數轉換成數組類型
data.unshift(event); //把事件對象放入數組的第一項
special = jQuery.event.special[type] || {}; //事件類型是否須要進行特殊化處理,好比:mousescroll事件
if(special.trigger && special.trigger.apply(elem,data) === false){ //若是事件類型已經有trigger方法,就調用它,若是返回結果是false,就直接返回。
return;
}
eventPath = [[elem, special.bindType || type]]; //規劃冒泡的路徑,從當前元素,一直到window
if(!onlyHandlers && !special.noBubble && !jQuery.isWindow(elem)){ //若是元素的事件類型沒有阻止冒泡,而且元素不是window對象,就要進行冒泡模擬。
bubbleType = special.delegateType || type; //獲得事件的類型
cur = elem.parentNode;
for(old = elem; cur ; cur = cur.parentNode){ //把當前元素的全部父元素,都進行一次事件類型的冒泡。假設div1(當前元素,觸發click事件)上面有一個div2,div2上面有body,body上面有html,html上面有document。那麼eventPath的數組是:[[div1,click],[div2,click],[body,click],[html,click],[document,click]]
eventPath.push([cur, bubbleType]);
old = cur;
}
if(old === (elem.ownerDocument || document)){ //當old爲document時,cur爲空,就退出循環
eventPath.push([old.defaultView || old.parentWindow || window, bubbleType]); //模擬冒泡到window對象
}
}
for(i=0;i<eventPath.length&&!event.isPropagationStopped(); i++){ //沿着上面規劃好的冒泡路線,把通過的元素節點的指定類型事件的回調逐一觸發執行
cur = eventPath[i][0]; //元素
event.type = eventPath[i][1] //事件類型
handle = (jQuery._data(cur,"events") || {})[event.type] && jQuery._data(cur,"handle");//先判斷在緩存系統中是否有此元素綁定的此事件類型的回調方法,若是有,就取出來。
if(handle){ //執行這些回調方法
handle.apply(cur,data);
}
handle = ontype && cur[ontype]; //若是有onXXX綁定的回調,不管是寫在js中,仍是html標籤內,都會取到
if(handle && jQuery.acceptData(cur) && handle.apply && handle.apply(cur, data) === false){//若是handle不爲空,而且當前元素能綁定數據,而且handle有apply方法,就執行handle回調方法,若是當前回調返回false,就阻止默認事件。
event.preventDefault();
}
}
event.type = type;
if(!onlyHandlers && !event.isDefaultPrevented()){ //若是沒有執行preventDefault方法,或上面的handle方法返回false(也會阻止默認事件)。就模擬默認行爲。具體是指模擬:submit,blur,focus,select,reset,scroll等方法。
if((!special._default || special._default.apply(elem.ownerDocument,data) === false) && //若是用戶指定了默認行爲,就執行指定的默認行爲,若是指定的默認行爲返回false,就繼續判斷下面的條件
!(type === "click" && jQuery.nodeName(elem, "a"))&& //若是事件類型是click,而且是在a標籤上觸發的,就不執行它的默認行爲了,不然繼續判斷
jQuery.acceptData(elem)){ //若是當前元素能夠綁定數據
if(ontype && elem[type] && //若是元素有onXXX回調,而且元素有type方法,好比:<input type ="submit" onsubmit="function(){}">,它有onXXX的回調方法,也有input.submit的方法,所以繼續判斷
((type!=="focus" && type!=="blur") || event.target.offsetWidth !==0) && //若是事件類型不是focus,blur。或事件類型是focu,blur,可是觸發這個事件的元素不是隱藏的(隱藏的元素,它的offsetWidth爲0),就繼續判斷。(觸發隱藏元素的focus和blur默認行爲,IE6-8下會拋出錯誤。)
!jQuery.isWindow(elem)){ //不是window元素就進入if語句執行默認行爲。(window的默認行爲觸發,會出現問題,好比:window.scroll方法,在IE與標準瀏覽器存在差別,IE會默認scroll()方法爲scroll(0,0))
old = elem[ontype];
if(old){
elem[ontype] = null; //onXXX的回調已經執行過了,就不用再次執行了
}
jQuery.event.triggered = type; //標識正在觸發此事件類型,防止下面的elem [ type ] ()重複執行dispatch方法
elem [ type ] (); //執行默認行爲,好比:input[submit](),input元素的提交方法。點擊類型爲submint的input,會默認執行submit屬性方法。可是這裏不會調用dispatch方法。
jQuery.event.triggered = undefined; //還原
if(old){
elem[ontype] = old;
}
}
}
}
return event.result;
}
籠統來講,trigger就是dispatch的增強版。dispatch只觸發當前元素與其底下元素(經過事件代理的方式)的回調。trigger則是模擬整個冒泡過程,除了它自身,還觸發其祖先節點與window的同類型的回調。不過從trigger的代碼來看,它比dispatch多作的事就是觸發事件的默認行爲。其實trigger要作的事就是在某一元素觸發一個回調(dispatch),而後讓它順勢冒泡,觸發其餘回調(dispatch)就好了。
瀏覽器提供了原生派發機制,IE下叫fireEvent,標準瀏覽器爲dispatchEvent。IE下的問題是,有許多事件不能冒泡或不能冒泡到頂層,若是咱們把IE的代碼獨立出來,標準瀏覽器用一套,IE用一套(舊版本IE,IE9在標準模式中支持dispatchEvent方法了),這樣性能大大提升。zepto.js就是這麼幹的。
在建立萬能事件對象時,理應document.createEvent("Events");但早期瀏覽器須要用document.createEvent("HTMLEvents");
萬能事件理應能觸發全部事件的回調,可是鼠標事件,要用MouseEvents參數建立鼠標對象事件才行。
火狐不支持mousewheel,咱們須要用document.createEvent("MouseScrollEvents")建立DOMMouseScroll事件對象。
加油!