解密jQuery事件核心 - 模擬事件(四)

前幾章已經把最核心的實現都分解過了,這一章我們看看jQuery是如何實現事件模擬的

在Internet Explorer 8和更低,一些事件change 和 submit本身不冒泡,但jQuery修改這些冒泡,創建一致的跨瀏覽器的行爲。

焦點事件

blur :

在這個事件觸發前,元素已經失去焦點,不冒泡,同步觸發。target 指向當前失去焦點的元素。

focus:

在這個事件觸發前,元素已經得到焦點,不冒泡,同步觸發。target 指向當前得到焦點的元素。

 


與此同時DOM Level 3 事件模塊 還定義了 focusin ,focusout 以及 DOMFocusIn ,DOMFocusOut 四個事件。

focusin :

在當前元素獲得焦點前以及相關元素失去焦點前觸發,可冒泡,同步觸發。target 指向當前將要獲得焦點的元素,relatedTarget 指向失去焦點的元素

focusout :

在當前失去焦點前觸發,可冒泡,同步觸發。target 指向當前將要失去焦點的元素,relatedTarget 指向將要失去焦點的元素。

DOMFocusIn :

在這個事件觸發前,元素已經得到焦點,可冒泡,同步觸發。target 指向當前得到焦點的元素。

DOMFocusOut :

在這個事件觸發前,元素已經沒有焦點,可冒泡,同步觸發。target 指向當前失去焦點的元素

 


事件的兼容性支持

image


1, 所有 IE 版本均支持focusin/focusout事件(注意:IE6/7/8中不支持el.addEventListener方法)。 
2, Opera 最強悍即支持attachEvent,又支持addEventListener。且這兩種方式添加事件均支持focusin/focusout事件。 
3, Safari/Chrome  給人一個驚喜,雖然el.onfocusin方式不支持,但 addEventListener方式卻支持。因此想讓Safari/Chrome中支持focusin事件,只能使用addEventListener方式添加事件。 
4, Firefox 任何一種添加事件方式都不支持 focusout/focuso

那麼如何在所有的平臺上都兼容focusin/focusout?

 


jQuery.event.special方法

這個方法在event.add,event.dispatch等幾個事件的處理地方都會被調用到,jQuert.event.special 對象用於某些事件類型的特殊行爲和屬性

換句話說就是某些事件不是大衆化的的事件,不能一概處理,比如 load 事件擁有特殊的 noBubble 屬性,可以防止該事件的冒泡而引發一些錯誤

所以需要單獨針對處理,但是如果都寫成判斷的形式,顯然代碼結構就不合理了,而且不方便提供給用戶自定義擴展

在webkit下的截圖,特殊事件類型

image

大體上針對9種事件,不同情況下處理hack,我們具體分析下焦點事件兼容冒泡處理,處理大同小異

 


jQuery.event 事件機制 focusin/ focusout 事件

image

針對focusin/ focusout 事件jQuery.event.special擴充2組處理機制,

special.setup方法主要是來在Firefox中模擬focusin和focusout事件的,因爲各大主流瀏覽器只有他不支持這兩個事件。

由於這兩個方法支持事件冒泡,所以可以用來進行事件代理

複製代碼
var attaches = 0,
    handler = function( event ) {
        jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
    };

jQuery.event.special[ fix ] = {
    setup: function() {
        if ( attaches++ === 0 ) {
            document.addEventListener( orig, handler, true );
        }
    },
    teardown: function() {
        if ( --attaches === 0 ) {
            document.removeEventListener( orig, handler, true );
        }
    }
};
複製代碼

前面的分析我們就知道通過事件最終都是通過add方法綁定的,也就是addEventListener方法綁定的,但是在add方法之前會有一個過濾分支

image

以前看不懂代碼,現在回過來恍然大悟了,原來這個方法是這樣用的

所以最終代碼會跑到各種的Hack中了,

可見對focusin/ focusout 的處理,沒有用通用的方法,而且是直接用的special.setup中的綁定

幾個重點

1 綁定的是focusin/ focusout 事件,內部確換成了focus/blur事件

2 document.addEventListener( orig, handler, true );事件綁在document上,最後是true,用的捕獲綁定

3 jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );方法

 


爲什麼用捕獲?

因爲火狐不支持focusin/ focusout事件,所以要找個所有瀏覽器都兼容類似事件,對了那就是focus/blur,

但是focus/blur不能冒泡丫,怎麼辦?

咱不是還有捕獲嗎?

那麼利用捕獲怎麼模擬出冒泡呢?

 


jQuery.event.simulate方法

複製代碼
jQuery.event.simulate = function( type, elem, event, bubble ) {
    // 重寫事件
    var e = jQuery.extend(
        new jQuery.Event(),
        event,
        { type: type,
            isSimulated: true,
            originalEvent: {}
        }
    );
    // 如果要冒泡
    if ( bubble ) {
        // 利用jQuery.event.trigger模擬觸發事件
        jQuery.event.trigger( e, null, elem );
    } else {
        // 否則利用jQuery.event.dispatch來執行處理
        jQuery.event.dispatch.call( elem, e );
    }
    // 如果需要阻止默認操作,則阻止
    if ( e.isDefaultPrevented() ) {
        event.preventDefault();
    }
}
複製代碼

可以看到focusin/ focusout 可冒泡事件實現原理是

1 focusin 事件添加事件處理程序時,jQuery 會在 document 上會添加 handler 函數

2 在事件捕獲階段監視特定元素的 focus/ blur 動作,捕獲行爲發生在 document 對象上,這樣纔能有效地實現所有元素都能可以冒泡的事件。

3 程序監視到存在 focus/ blur 行爲,就會觸發綁定在 document 元素上的事件處理程序,該事件處理程序在內部調用 simulate 邏輯觸發事件冒泡,以實現我們希望的可以冒泡事件。

之後利用jQuery.event.trigger模擬觸發事件,把從target-document的元素都過濾出來,分析每個節點上是否綁定了事件句柄,依次處理,按照一定的規範,比如是否有事件阻止之類的,這裏就不再重複分析了jQuery.event.trigger  http://www.cnblogs.com/aaronjs/p/3452279.html

 


總結

咋一看其實原理都挺簡單的, 但是jQuery爲了實現兼容統一,可謂煞費苦心了,把事件冒泡與捕獲都統一模擬了一遍

  1. jQuery爲統一原生Event對象而封裝的jQuery.Event類,封裝了preventDefault,stopPropagation,stopImmediatePropagation原生接口,可以直接捕獲到用戶的行爲
  2. 由核心組件 jQuery.cache 實現註冊事件處理程序的存儲,實際上綁定在 DOM元素上的事件處理程序只有一個,即 jQuery.cache[elem[expando]].handle 中存儲的函數,該函數在內部調用 jQuery.event.dispatch(event) 實現對該DOM元素特定事件的緩存的訪問,並依次執行這些事件處理程序。
  3. jQuery.event.add(elem, types, handler, data, selector) 方法用於給特定elem元素添加特定的事件 types([type.namespace, type.namespace, ...])的事件處理程序 handler, 通過第四個參數 data 增強執行當前 handler 事件處理程序時的 $event.data 屬性,以提供更靈活的數據通訊,而第五個元素用於指定基於選擇器的委託事件
  4. namespace 命名空間機制,namespace 機制可以對事件進行更爲精細的控制,開發人員可以指定特定空間的事件,刪除特定命名空間的事件,以及觸發特定命名空間的事件。這使得對事件處理機制的功能更加健
  5. jQuert.event.special 對象用於某些事件類型的特殊行爲和屬性。比如 load 事件擁有特殊的 noBubble 屬性,可以防止該事件的冒泡而引發一些錯誤。總的來說,有這樣一些方法和屬性:
  6. jQuery.event.simulate(type, elem, event, bubble)模擬事件並立刻觸發方法,可用於在DOM元素 elem 上模擬自定義事件類型 type,參數 bubble用於指定該事件是否可冒泡,event 參數表示 jQuery 事件對象 $event。 模擬事件通過事件對象的isSimulated屬性爲 true 表示這是模擬事件。該方法內部調用 trigger() 邏輯 或 dispatch() 邏輯立刻觸發該模擬事件。該方法主要用於修正瀏覽器事件的兼容性問題,比如模擬出可冒泡的 focusin/ focusout 事件,修正IE中 change 事件的不可冒泡問題,修正IE中 submit事件不可冒泡問題
  7. jQuery.event.dispatch(event) 方法在處理事件委託機制時,依賴委託節點在DOM樹的深度安排優先級,委託的DOM節點層次越深,其執行優先級越高。而其對於stopPropagation的處理有些特殊,在事件委託情況下並不一定會調用綁定在該DOM元素上的該類型的所有事件處理程序,而依賴於委託的事件處理程序的執行結果,如果低層委託的事件處理程序聲明瞭停止冒泡,那麼高層委託的事件以及自身綁定事件就不會被執行,這拓展了 DOM 委託機制的功能。
  8. jQuery.event.trigger(event | type, data, elem, onlyHandlers) 方法提供開發人員以程序方式觸發特定事件的接口,該方法的第一個參數可以是 $event/ event 對象 ,也可以是某個事件類型的字符串 type; 第二個參數 data 用於擴展該事件觸發時事件處理程序的參數規模,用於傳遞一些必要的信息。 elem參數表示觸發該事件的DOM元素;最後該方法在默認情況下,其事件會冒泡,並且在有默認動作的情況下執行默認行爲,但是如果指定了 onlyHandlers 參數,該方法只會觸發綁定在該DOM元素上的事件處理程序,而不會引發冒泡和默認動作,也不會觸發特殊的 trigger 行爲。
發佈了56 篇原創文章 · 獲贊 11 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章