所謂自定義事件,就是有別于帶有瀏覽器特定行為的事件(鼠標事件,鍵盤事件,html事件等),事件名稱可以自定義,可以通過特定的方法進行添加,觸發以及刪除。自定義事件相當于是 觀察者模式 ,可以把復雜邏輯解耦,代碼可以寫的很清晰,而且很容易復用。
一.JS事件應用初印象
我覺得更為準確的說,下面列舉的是Dom事件的應用。我們在這里可以通過了解JS關于Dom事件的綁定,觸發,解綁來一步一步書寫我們自己的JS自定義事件。關于自定義事件,我決定分兩步講解。第一步,也就是這篇文章將要提到的循序漸進的一步一步完善自定義JS事件。第二步,我們封裝一個與Dom元素有關的自定義事件函數。
// 事件綁定
function addEvent(element, eType, handler, bol) {
if(element.addEventListener){ //如果支持addEventListener
element.addEventListener(eType, handler bol);
}else if(element.attachEvent){ //如果支持attachEvent
element.attachEvent("on"+eType, handler);
}else{ //否則使用兼容的onclick綁定
element["on"+eType] = handle;
}
}
// 事件解綁
function removeEvent(element, eType, handler, bol) {
if(element.addEventListener){
element.removeEventListener(eType, handler, bol);
}else if(element.attachEvent){
element.detachEvent("on"+eType, handler);
}else{
element["on"+eType] = null;
}
}
//實例應用
var patty=document.getElementById("patty");
var sayHello=function (){
alert("Hello!!!");
}
addEvent(patty,click,sayHello,false); //添加點擊事件
這里我們關注下click事件的實現。我們可以很清楚的將其分成三部分:添加,觸發,刪除。
同時,我們必須考慮一個問題:事件和其處理函數應該怎樣存儲。事件分為很多種,click, mouseover, submit, keydown等等,每一種事件下又可以添加處理函數。這種一對多的映射關系,我們可以很自然想到用下面這樣數據結構來存儲事件。
_listener = {
"click": [func1, func2],
"custom": [func3],
...
}
二.自定義我們自己的JS事件
1.函數式實現
var _listener = {}; //存儲事件和其處理函數
var addEvent = function(type, fn) {
//添加
};
var fireEvent = function(type) {
//觸發
};
var removeEvent = function(type, fn) {
//刪除
};
//添加eat事件
addEvent("eat",function(){
alert("eat an apple!");
})
// 觸發自定義eat事件
fireEvent(eat);
我們沒有詳細展示函數式實現的代碼,是因為這種寫法較為基礎且過多地暴露全局變量,我們稍微了解即可,具體函數地實現,我們在下面的方式會實現。
2.用字面量方式實現
var Event = {
_listeners: {},
// 添加
addEvent: function(type, fn) {
if (typeof this._listeners[type] === "undefined") {
this._listeners[type] = [];
}
if (typeof fn === "function") {
this._listeners[type].push(fn);
}
return this;
},
// 觸發
fireEvent: function(type) {
var arrayEvent = this._listeners[type];
if (arrayEvent instanceof Array) {
for (var i=0, length=arrayEvent.length; i<length; i+=1) {
if (typeof arrayEvent[i] === "function") {
arrayEvent[i]({ type: type });
}
}
}
return this;
},
// 刪除
removeEvent: function(type, fn) {
var arrayEvent = this._listeners[type];
if (typeof type === "string" && arrayEvent instanceof Array) {
if (typeof fn === "function") {
// 清除當前type類型事件下對應fn方法
for (var i=0, length=arrayEvent.length; i<length; i+=1){
if (arrayEvent[i] === fn){
this._listeners[type].splice(i, 1);
break;
}
}
} else {
// 如果僅僅參數type, 或參數fn邪魔外道,則所有type類型事件清除
delete this._listeners[type];
}
}
return this;
}
};
//添加eat事件
Event.addEvent("eat",function(){
alert("eat an apple!");
})
// 觸發自定義eat事件
Event.fireEvent(eat);
字面量實現雖然減少了全局變量,但是其屬性方法等都是暴露而且都是唯一的,一旦某個關鍵屬性(如_listeners)不小心在某事件處reset了,則整個全局的自定義事件都會崩潰。因此,我們可以進一步改進,例如,使用原型鏈繼承,讓繼承的屬性(如_listeners)即使出問題也不會影響全局。
3.原型模式實現
var EventTarget = function() {
this._listener = {};
};
EventTarget.prototype = {
constructor:EventTarget,
addEvent: function(type, fn) {
if (typeof type === "string" && typeof fn === "function") {
if (typeof this._listener[type] === "undefined") {
this._listener[type] = [fn];
} else {
this._listener[type].push(fn);
}
}
return this;
},
addEvents: function(obj) {
obj = typeof obj === "object"? obj : {};
var type;
for (type in obj) {
if ( type && typeof obj[type] === "function") {
this.addEvent(type, obj[type]);
}
}
return this;
},
fireEvent: function(type) {
if (type && this._listener[type]) {
var events = {
type: type,
target: this
};
for (var length = this._listener[type].length, start=0; start<length; start+=1) {
this._listener[type][start].call(this, events);
}
}
return this;
},
fireEvents: function(array) {
if (array instanceof Array) {
for (var i=0, length = array.length; i<length; i+=1) {
this.fireEvent(array[i]);
}
}
return this;
},
removeEvent: function(type, key) {
var listeners = this._listener[type];
if (listeners instanceof Array) {
if (typeof key === "function") {
for (var i=0, length=listeners.length; i<length; i+=1){
if (listeners[i] === key){
listeners.splice(i, 1);
break;
}
}
} else if (key instanceof Array) {
for (var lis=0, lenkey = key.length; lis<lenkey; lis+=1) {
this.removeEvent(type, key[lenkey]);
}
} else {
delete this._listener[type];
}
}
return this;
},
removeEvents: function(params) {
if (params instanceof Array) {
for (var i=0, length = params.length; i<length; i+=1) {
this.removeEvent(params[i]);
}
} else if (typeof params === "object") {
for (var type in params) {
this.removeEvent(type, params[type]);
}
}
return this;
}
};
//使用
var event1=new EventTarget();
var event2=new EventTarget();
這樣我們發現,event1和event2在共享方法的同時,又有自己的_listener 屬性,彼此之間不會污染。
我相信大家通過上面的了解,對JS自定義事件一定有更深的理解了,OK這次就先講這么多,謝謝觀看!