自定义对象上的addEventListener

我创build了一个有几个方法的对象。 其中一些方法是asynchronous的,因此我想使用事件来完成方法时执行的操作。 为此,我尝试将addEventListener添加到对象。

的jsfiddle

var iSubmit = { addEventListener: document.addEventListener || document.attachEvent, dispatchEvent: document.dispatchEvent, fireEvent: document.fireEvent, //the method below is added for completeness, but is not causing the problem. test: function(memo) { var name = "test"; var event; if (document.createEvent) { event = document.createEvent("HTMLEvents"); event.initEvent(name, true, true); } else { event = document.createEventObject(); event.eventType = name; } event.eventName = name; event.memo = memo || { }; if (document.createEvent) { try { document.dispatchEvent(event); } catch (ex) { iAlert.debug(ex, 'iPushError'); } } else { document.fireEvent("on" + event.eventType, event); } } } iSubmit.addEventListener("test", function(e) { console.log(e); }, false); //This call is added to have a complete test. The errors is already triggered with the line before this one. iSubmit.test(); 

这将返回一个错误: Failed to add eventlisterens: TypeError: 'addEventListener' called on an object that does not implement interface EventTarget."

现在这个代码将被用于phonegap应用程序,当我这样做,它正在android / ios上工作。 但是在testing过程中,如果能够在至less一个浏览器中工作,那将是一件好事。

PS>我知道我可以启用冒泡,然后听取文档根目录,但是我想只有一点OOP,每个对象都可以独立工作。

addEventListener用于实现特定事件相关接口的DOM元素。 如果你想要一个纯JavaScript对象的事件系统,你正在寻找一个自定义的事件系统。 一个例子是Backbone.Events中的Backbone.Events。 基本的想法是使用一个对象作为散列来跟踪注册的callback。

我个人使用这个: https : //github.com/component/emitter

这是一个相当简单和优雅的解决scheme – 用on()off()emit()简短的方法命名。 您可以使用new Emitter()创build新实例,也可以使用Emitter(obj)将事件function混合到现有对象中。 注意这个库是为了与CommonJS模块系统一起使用而编写的,但是可以通过移除module.exports = ...行来在其他地方使用它。

如果你想听一个JavaScript对象,你有三种方法:

  • 使用在JavaScript中有很多实现的 子/酒吧模式
  • 或者通过Object get/set operatorsObject.definePropertyObject.prototype.watchProxy API使用本地实现
  • 使用Object.observe 。 运行Chrome 25+(2014年1月)。 但在2016年被弃用

关于sup / pub模式:

你需要发布事件。

关于本地实现:

  • Object get/set operators足以侦听添加,删除,更改,获取事件。 运营商有很好的支持 。 问题只在IE8-。 但是如果你想在IE8中使用get / set,使用Object.defineProperty而不是DOM对象或者使用Object.defineProperty sham 。
  • Object.prototype.watch有很好的ES5 Object.prototype.watch
  • Proxy API需要ES Harmony支持。

Object.observe示例

 var o = {}; Object.observe(o, function (changes) { changes.forEach(function (change) { // change.object contains changed object version console.log('property:', change.name, 'type:', change.type); }); }); ox = 1 // property: x type: add ox = 2 // property: x type: update delete ox // property: x type: delete 

如果你不需要真正的事件function(如冒泡,停止传播),那么你可以实现自己的事件。 addEventListener只是DOM的一个API,所以你不需要它在DOM之外的自己的对象。 如果你想围绕一个对象创build一个均匀的模式,这是一个不需要额外的浏览器API的好方法,而且应该是向后兼容的。

假设你有一个对象,当你调用dispatch方法的时候,你需要触发一堆事件:

 var OurDispatcher, dispatcher; OurDispatcher = (function() { function OurDispatcher() { this.dispatchHandlers = []; } OurDispatcher.prototype.on = function(eventName, handler) { switch (eventName) { case "dispatch": return this.dispatchHandlers.push(handler); case "somethingElse": return alert('write something for this event :)'); } }; OurDispatcher.prototype.dispatch = function() { var handler, i, len, ref; ref = this.dispatchHandlers; for (i = 0, len = ref.length; i < len; i++) { handler = ref[i]; setTimeout(handler, 0); } }; return OurDispatcher; })(); dispatcher = new OurDispatcher(); dispatcher.on("dispatch", function() { return document.body.innerHTML += "DISPATCHED</br>"; }); dispatcher.on("dispatch", function() { return document.body.innerHTML += "DISPATCHED AGAIN</br>"; }); dispatcher.dispatch(); 

大多数情况下,并不一定比这更复杂。 通过这种方式,您可以很好地控制事件,并且无需担心向后兼容性问题或外部库问题,因为所有内容都得到广泛支持。 从技术上讲,你甚至可以不用setTimeout来处理你的callback,而不需要任何API。 任何其他像stopPropagation()将不得不自己处理。

https://jsfiddle.net/ozsywxer/

当然,CustomEvent有polyfills,但除非需要高级事件function,否则我更愿意将自己的事件系统包装到一个“类”中,并使用它来扩展其他类/函数。

下面是CoffeeScript的版本,这是JavaScript的派生: https : //jsfiddle.net/vmkkbbxq/1/

^^有点容易理解。

有两个问题。

首先, iSubmit.addEventListener()方法实际上是EventTarget DOM接口上的一个方法:

  • 事件目标
  • EventTarget#addEventListener()

这些意图仅用于DOM元素。 通过将其作为一个方法添加到iSubmit对象,您将在一个不是EventTarget的对象上调用它。 这就是为什么Chrome会抛出Uncaught TypeError: Illegal invocation JavaScript错误。

第一个问题是关键的,但是如果你可以使用EventTarget#addEventListener()你的代码将不起作用,因为事件被添加到iSubmit但是从document派发。 通常,在附加事件监听器和派发事件时,需要使用同一个对象的方法(除非您使用冒泡事件,这是一个不同的故事 – 注意:冒泡不限于JavaScript或DOM相关的事件)。

使用自定义事件与自己的对象是非常正常的。 正如Evan Yu所说 ,有这样的图书馆。 这里有一对夫妇:

  • millermedeiros / js-signals
  • Wolfy87 / EventEmitter

我已经使用了js-signals并且喜欢它。 我从来没有使用Wolfy87/EventEmitter ,但它有一个很好的外观。

如果您使用js-signals您的示例可能如下所示

的jsfiddle

 var iSubmit = { finished: new signals.Signal(), test: function test(memo) { this.finished.dispatch(memo || {}); } }; iSubmit.finished.add(function(data) { console.log('finished:', data); }); iSubmit.test('this is the finished data'); // alternatively iSubmit.finished.dispatch('this is dispatched directly from the signal'); 

如果您在Node.js环境中,那么您可以使用Node的EventEmitter类 :

CustomObject.js

 const EventEmitter = require('events'); class CustomObject extends EventEmitter { constructor() { super(); } doSomething() { const event = {message: 'Hello World!'}; this.emit('myEventName', event); } } module.exports = CustomObject; 

用法:

 const CustomObject = require('./CustomObject'); // 1. Create a new instance const myObject = new CustomObject(); // 2. Subscribe to events with ID "myEventName" myObject.on('myEventName', function(event) { console.log('Received event', event); }); // 3. Trigger the event emitter myObject.doSomething(); 

如果您想在Node.js环境外使用Node的EventEmitter,那么您可以使用webpack (最好是v2.2或更高版本)将您的CustomClass与一个EventEmitter polyfill(由webpack构build)一起使用。

这是它是如何工作的(假设你使用npm install -g webpack在全局npm install -g webpack ):

  1. 运行webpack CustomObject.js bundle.js --output-library=CustomObject
  2. 在你的HTML页面中包含bundle.js (它会暴露window.CustomObject
  3. 没有第三步!

的index.html

 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Title</title> <script src="bundle.js"></script> </head> <body> <script> // 1. Create a new instance const myObject = new window.CustomObject(); // 2. Subscribe to events with ID "myEventName" myObject.on('myEventName', function(event) { console.log('Received event', event); }); // 3. Trigger the event emitter myObject.doSomething(); </script> </body> </html> 

本文解释了创build自定义事件: http : //www.sitepoint.com/javascript-custom-events/

这里是一个例子:

创build活动 –

 var event = new CustomEvent( "newMessage", { detail: { message: "Hello World!", time: new Date(), }, bubbles: true, cancelable: true } ); 

分配事件的东西 –

 document.getElementById("msgbox").dispatchEvent(event); 

订阅事件 –

 document.addEventListener("newMessage", newMessageHandler, false); 

只是猜测; 我没有自己尝试过。 但您可以创build一个虚拟元素,并在虚拟元素上触发/侦听事件。 另外,我更喜欢没有图书馆。

 function myObject(){ //create "dummy" element var dummy = document.createElement('dummy'); //method for listening for events this.on = function(event, func){dummy.addEventListener(event, func);}; //you need a way to fire events this.fireEvent = function(event, obj){ dummy.dispatchEvent(new CustomEvent(event, {detail: obj})); } } //now you can use the methods in the object constructor var obj = new myObject(); obj.on("custom", function(e){console.log(e.detail.result)}); obj.fireEvent("custom", {result: "hello world!!!"}); 

我认为你可以使用Object $ Deferred和promise。 它会让你做这样的事情:

堆叠:将应用程序中任何位置的多个处理程序绑定到相同的承诺事件。

  var request = $.ajax(url); request.done(function () { console.log('Request completed'); }); 

//在应用程序中的其他地方

  request.done(function (retrievedData) { $('#contentPlaceholder').html(retrievedData); }); 

并行任务:请求多个承诺返回一个承诺,告知他们相互完成。

 $.when(taskOne, taskTwo).done(function () { console.log('taskOne and taskTwo are finished'); }); 

顺序任务:按顺序执行任务。

  var step1, step2, url; url = 'http://fiddle.jshell.net'; step1 = $.ajax(url); step2 = step1.then( function (data) { var def = new $.Deferred(); setTimeout(function () { console.log('Request completed'); def.resolve(); },2000); return def.promise(); }, function (err) { console.log('Step1 failed: Ajax request'); } ); step2.done(function () { console.log('Sequence completed') setTimeout("console.log('end')",1000); }); 

来源: http : //blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt2-practical-use