所谓自定义事件,就是有别于带有浏览器特定行为的事件(鼠标事件,键盘事件,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这次就先讲这么多,谢谢观看!