JavaScript 自定义事件的实现
自定义事件,就是有别于有别于带有浏览器特定行为的事件(类似 click, mouseover, submit, keydown 等事件),事件名称可以随意定义,可以通过特定的方法进行添加,触发以及删除。
模拟实现
1. 字面量实现
// 实现 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. 原型模式实现
// 实现 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. 创建自定义事件
var event = new Event('build'); // Listen for the event. elem.addEventListener('build', function (e) { ... }, false); // Dispatch the event. elem.dispatchEvent(event);
添加自定义数据 – CustomEvent()
var event = new CustomEvent('build', { 'detail': elem.dataset.time });
下面的代码允许你在事件监听器中访问更多的数据:
function eventHandler(e) { log('The time is: ' + e.detail); }
2. 老式方法(已过时,此处仅作参考)
// 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);
参考文档: