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

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

在Internet Explorer 8和更低,一些事件changesubmit自己不冒泡,但jQuery修改這些冒泡,建立一致的跨瀏覽器的行爲。web

焦點事件

blur :瀏覽器

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

focus:函數

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

 


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

focusin :代理

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

focusout :htm

在當前失去焦點前觸發,可冒泡,同步觸發。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 邏輯觸發事件冒泡,以實現咱們但願的能夠冒泡事件。

4 以後利用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 行爲。
  9. …………………
相關文章
相關標籤/搜索