自定义事件,就是有别于有别于带有浏览器特定行为的事件(类似 click, mouseover, submit, keydown 等事件),事件名称可以随意定义,可以通过特定的方法进行添加,触发以及删除。
模拟实现
1. 字面量实现
代码语言:javascript复制// 实现
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;
}
};
// 调用
Event.addEvent("alert", function() {
alert("弹出!");
});
// 触发自定义 alert 事件
Event.fireEvent("alert");
缺点:字面量实现其属性方法等都是暴露而且都是唯一的,一旦某个关键属性(如 _listeners)不小心在某事件处 reset 了下,则整个全局的自定义事件都会崩溃。
2. 原型模式实现
代码语言:javascript复制// 实现
var EventTarget = function() {
this._listener = {};
};
EventTarget.prototype = {
constructor: this,
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 myEvents = new EventTarget();
var yourEvents = new EventTarget();
浏览器 API 实现
1. 创建自定义事件
代码语言:javascript复制var event = new Event('build');
// Listen for the event.
elem.addEventListener('build', function (e) { ... }, false);
// Dispatch the event.
elem.dispatchEvent(event);
添加自定义数据 – CustomEvent()
代码语言:javascript复制var event = new CustomEvent('build', { 'detail': elem.dataset.time });
下面的代码允许你在事件监听器中访问更多的数据:
代码语言:javascript复制function eventHandler(e) {
log('The time is: ' e.detail);
}
2. 老式方法(已过时,此处仅作参考)
代码语言:javascript复制// Create the event.
var event = document.createEvent('Event');
// Define that the event name is 'build'.
event.initEvent('build', true, true);
// Listen for the event.
document.addEventListener('build', function (e) {
// e.target matches document from above
}, false);
// target can be any Element or other EventTarget.
document.dispatchEvent(event);
参考文档:
- 漫谈js自定义事件、DOM/伪DOM自定义事件
- 创建和触发 events