JS自定義事件的定義和觸發(createEvent, dispatchEvent)

4、僞DOM自定義事件

這裏的「僞DOM自定義事件」是本身定義的一個名詞,用來區分DOM自定義事件的。例如jQuery庫,其是基於包裝器(一個包含DOM元素的中間層)擴展事件的,既與DOM相關,又不直接是DOM,所以,稱之爲「僞DOM自定義事件」。javascript

//zxx: 下面即將展現的代碼目的在於學習與認識,要想實際應用可能還須要在細節上作些調整。例如,下面測試的包裝器僅僅只是包裹DOM元素,並不是選擇器之類;$符號未增長衝突處理,且幾個重要方法都暴露在全局環境中,沒有閉包保護等。java

原型以及new函數構造不是本文重點,所以,下面這個僅展現:node

var $ = function(el) {
    return new _$(el);    
};
var _$ = function(el) {
    this.el = el;
};
_$.prototype = {
    constructor: this,
    addEvent: function() {
        // ...
    },
    fireEvent: function() {
        // ...
    },
    removeEvent: function() {
        // ...
    }
}

因而咱們就可使用相似$(dom).addEvent()的語法爲元素添加事件了(包括不包含瀏覽器行爲的自定義事件)。瀏覽器

自定義事件的添加
若是隻考慮事件添加,咱們的工做其實很簡單,根據支持狀況,addEventListenerattachEvent方法分別添加事件(attachEvent方法後添加事件先觸發)便可:閉包

addEvent: function(type, fn, capture) {
    var el = this.el;
    if (window.addEventListener) {
        el.addEventListener(type, fn, capture);        
    } else if (window.attachEvent) {
        el.attachEvent("on" + type, fn);
    }
    return this;
}

顯然,事情不會這麼簡單,有句古話叫作「上山容易下山難」,自定義事件添加容易,可是如何觸發它們呢?——考慮到自定義事件與瀏覽器行爲無關,同時瀏覽器沒有直接的觸發事件的方法。dom

自定義事件的觸發
又是不可避免的,因爲瀏覽器兼容性問題,咱們要分開說了,針對標準瀏覽器和IE6/7等考古瀏覽器。函數

1. 對於標準瀏覽器,其提供了可供元素觸發的方法:element.dispatchEvent(). 不過,在使用該方法以前,咱們還須要作其餘兩件事,及建立和初始化。所以,總結說來就是:學習

document.createEvent()
event.initEvent()
element.dispatchEvent()

舉個板栗:測試

$(dom).addEvent("alert", function() {
    alert("彈彈彈,彈走魚尾紋~~");
});

// 建立
var evt = document.createEvent("HTMLEvents");
// 初始化
evt.initEvent("alert", false, false);

// 觸發, 即彈出文字
dom.dispatchEvent(evt);

createEvent()方法返回新建立的Event對象,支持一個參數,表示事件類型,具體見下表:this

參數 事件接口 初始化方法
HTMLEvents HTMLEvent initEvent()
MouseEvents MouseEvent initMouseEvent()
UIEvents UIEvent initUIEvent()

關於createEvent()方法我本身瞭解也不是很深刻,不想濫竽充數,誤人子弟,因此您有疑問我可能做答不了,但願對熟知該方法的人能夠作進一步的解釋說明(例如事件接口與document關係,UIEvent是什麼東西等)。

initEvent()方法用於初始化經過DocumentEvent接口建立的Event的值。支持三個參數:initEvent(eventName, canBubble, preventDefault). 分別表示事件名稱,是否能夠冒泡,是否阻止事件的默認操做。

dispatchEvent()就是觸發執行了,dom.dispatchEvent(eventObject), 參數eventObject表示事件對象,是createEvent()方法返回的建立的Event對象。

2. 對於IE瀏覽器,因爲向下不少版本的瀏覽器都不支持document.createEvent()方法,所以咱們須要另闢蹊徑(聽說IE有document.createEventObject()event.fireEvent()方法,可是不支持自定義事件~~)。

IE瀏覽器有很多自給自足的東西,例以下面要說的這個"propertychange"事件,顧名思意,就是屬性改變即觸發的事件。例如文本框value值改變,或是元素id改變,或是綁定的事件改變等等。

咱們能夠利用這個IE私有的東西實現自定義事件的觸發,你們能夠先花幾分鐘想一想……

// zxx: 假設幾分鐘已通過去了……

你們如今有思路了沒?其實說穿了很簡單,當咱們添加自定義事件的時候,順便給元素添加一個自定義屬性便可。例如,咱們添加自定義名爲"alert"的自定義事件,順便咱們能夠對元素作點小手腳:

dom.evtAlert = "2012-04-01";

再順便把自定義事件fn塞到"propertychange"事件中:

dom.attachEvent("onpropertychange", function(e) {
    if (e.propertyName == "evtAlert") {
        fn.call(this);
    }
});

這個,當咱們須要觸發自定義事件的時候,只要修改DOM上自定義的evtAlert屬性的值便可:

dom.evtAlert = Math.random();	// 值變成隨機數

此時就會觸發dom上綁定的onpropertychange事件,又由於修改的屬性名正好是"evtAlert", 因而自定義的fn就會被執行。這就是IE瀏覽器下事件觸發實現的完整機制,應該說講得仍是蠻細的。

自定義事件的刪除
與觸發事件不一樣,事件刪除,各個瀏覽器都提供了對於的時間刪除方法,如removeEventListenerdetachEvent。不過呢,對於IE瀏覽器,還要多刪除一個事件,就是爲了實現觸發功能額外增長的onpropertychange事件:

dom.detachEvent("onpropertychange", evt);
var $ = function(el) {
    return new _$(el);    
};
var _$ = function(el) {
    this.el = (el && el.nodeType == 1)? el: document;
};
_$.prototype = {
    constructor: _$,
    addEvent: function(type, fn, capture) {
        var el = this.el;
        
        if (window.addEventListener) {
            el.addEventListener(type, fn, capture);

            var ev = document.createEvent("HTMLEvents");
            ev.initEvent(type, capture || false, false);
            // 在元素上存儲建立的事件,方便自定義觸發
            if (!el["ev" + type]) {
                el["ev" + type] = ev;
            }
            
        } else if (window.attachEvent) {
            el.attachEvent("on" + type, fn);    
            if (isNaN(el["cu" + type])) {
                // 自定義屬性,觸發事件用
                el["cu" + type] = 0; 
            }
            
            var fnEv = function(event) {
                if (event.propertyName == "cu" + type) {
                    fn.call(el);
                }
            };
            
            el.attachEvent("onpropertychange", fnEv);
            
            // 在元素上存儲綁定的propertychange事件,方便刪除
            if (!el["ev" + type]) {
                el["ev" + type] = [fnEv];
            } else {
                el["ev" + type].push(fnEv);    
            }
        }
        
        return this;
    },
    fireEvent: function(type) {
        var el = this.el;
        if (typeof type === "string") {
            if (document.dispatchEvent) {
                if (el["ev" + type]) {
                    el.dispatchEvent(el["ev" + type]);
                }
            } else if (document.attachEvent) {
                // 改變對應自定義屬性,觸發自定義事件
                el["cu" + type]++;
            }    
        }    
        return this;
    },
    removeEvent: function(type, fn, capture) {
        var el = this.el;
        if (window.removeEventListener) {
            el.removeEventListener(type, fn, capture || false);
        } else if (document.attachEvent) {
            el.detachEvent("on" + type, fn);
            var arrEv = el["ev" + type];
            if (arrEv instanceof Array) {
                for (var i=0; i<arrEv.length; i+=1) {
                    // 刪除該方法名下全部綁定的propertychange事件
                    el.detachEvent("onpropertychange", arrEv[i]);
                }
            }
        }
        return this;    
    }
};
相關文章
相關標籤/搜索