AddEventListener to user object

I created an object that has several methods. Some of these methods are asynchronous, and so I want to use events to be able to perform actions after the methods have completed. For this, I tried to add addEventListener to the object.

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 are already triggered with the line before this one. iSubmit.test(); 

This will return an error: not Failed to add eventlisterens: TypeError: 'addEventListener' called on an object that does not implement interface EventTarget."

Now this code will be used in the PhoneGap application, and when I do it, it works on Android / IOS. However, during testing, it would be nice if I could get it to work in at least one browser.

PS> I know that I could turn on the popup and then listen to the root of the document, but I would like to have a little OOP, where each object can work on its own.

+54
javascript object cordova addeventlistener
Dec 30 '14 at 8:13
source share
11 answers

addEventListener is for DOM elements that implement specific event-related interfaces. If you need an event system on pure JavaScript objects, you are looking for your own event system. An example is Backbone.Events in Backbone.js. The basic idea is to use an object as a hash to track registered callbacks.

Personally, I use this: emitter .

This is a fairly simple and elegant solution - with short method names such as on() , off() and emit() . you can create new instances using new Emitter() or use Emitter(obj) to mix event capabilities with existing objects. Please note that this library is written for use with the CommonJS modular system, but you can use it anywhere by removing the line module.exports = ...

+46
Jan 03 '14 at
source share

If you want to listen to a javascript object, you have three ways:

About the sup / pub pattern:

You need to publish events.

About embedded implementations:

  • Object get/set operators enough to listen, add, delete, receive events. Operators have good support . Problems only in IE8-. But if you want to use get / set in IE8, use Object.defineProperty , but on DOM objects or use Object.defineProperty sham .
  • Object.prototype.watch has a nice ES5 polyfill .
  • Proxy API requires ES Harmony support.

Object.observe example

 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 
+15
Jan 08 '14 at 20:51
source share

If you don't need true event functions (e.g. bubbles, stopPropagation), you can implement your own events. addEventListener is just a DOM API, so you really don't need it for your own objects outside of the DOM. If you want to create a linked template around an object, here is a good way to do it, which does not require any additional browser APIs, and should be very reverse.

Let's say you have an object where you want a bunch of events to be included when the send method starts:

 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(); 

In fact, it should not be more complicated than that, for the most part. Thus, you have decent control over your events, and you do not need to worry about backward compatibility or external libraries, because everything there is widely supported. Technically, you could do without setTimeout and handle your callbacks without any APIs. Anything, such as stopPropagation (), will need to be processed independently.

https://jsfiddle.net/ozsywxer/

Of course, there are policies for CustomEvent, but if I do not need advanced event functions, I prefer to transfer my own event system to a class and extend other classes / functions with it.

Here's the CoffeeScript version that JavaScript comes from: https://jsfiddle.net/vmkkbbxq/1/

^^ A bit easier to understand.

+15
Jun 30 '15 at 19:12
source share

There are two problems.

Firstly, the iSubmit.addEventListener() method is actually a method on the DOM EventTarget interface:

They are intended to be used only for DOM elements. By adding it to an iSubmit object as a method, you call it on an object that is not an EventTarget . This is why Chrome Uncaught TypeError: Illegal invocation JavaScript error.

The first problem is critical, but if you can use EventTarget#addEventListener() , your code will not work because the event is added to iSubmit , but dispatched from document . As a rule, the same object methods must be used when connecting event listeners and dispatching events (if you do not use bubble events, which are different stories - Note: the bubble is not limited to events related to JavaScript or DOM, for example ).

Using custom events with your own objects is very normal. As Ewan Yu said, there are libraries for this. Here is a couple:

I used js-signals and it seems to be a bit. I never used Wolfy87/EventEmitter , but he looked at it well.

Your example might look something like this if you used 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'); 
+10
Jan 04 '14 at 9:06
source share

If you are in a Node.js environment, you can use the Node EventEmitter class :

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; 

Application:

 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(); 

If you want to use the Node EventEmitter outside the Node.js environment, you can use webpack (preferably v2.2 or later) to get your CustomClass package along with the EventEmitter polyfill (created by the web package).

Here's how it works (assuming you installed webpack globally with npm install -g webpack ):

  • Run webpack CustomObject.js bundle.js --output-library=CustomObject
  • Include bundle.js in your HTML page (it will output window.CustomObject )
  • There is no third step!

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> 
+4
Feb 05 '17 at 1:54 on
source share

Just speculation; I have not tried it myself. But you can create a dummy element and trigger / listen to events on the dummy element. In addition, I prefer to go without libraries.

 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!!!"}); 
+4
Jun 03 '17 at 1:08 on
source share

This article explains how to create custom events: http://www.sitepoint.com/javascript-custom-events/

here is an example:

create an event -

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

assign an event to something -

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

subscribe to an event -

 document.addEventListener("newMessage", newMessageHandler, false); 
+1
Jan 02
source share

Usage: jsfiddle

This is a naive approach, but it may work for some applications:

 CustomEventTarget.prototype = { 'constructor': CustomEventTarget, on: function( ev, el ) { this.eventTarget.addEventListener( ev, el ) }, off: function( ev, el ) { this.eventTarget.removeEventListener( ev, el ) }, emit: function( ev ) { this.eventTarget.dispatchEvent( ev ) } } function CustomEventTarget() { this.eventTarget = new EventTarget } 
+1
Mar 15 '18 at 1:09
source share

I think you can use Object $ Deferred and promises. This will allow you to do something like this:

Stacked: Bind multiple handlers anywhere in the application to the same promise.

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

// Somewhere else in the application

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

Parallel tasks: ask several promises to return a promise that warns of their mutual completion.

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

Sequential tasks: perform tasks in a sequential order.

  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); }); 

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

0
Jan 02 '14 at 2:03
source share

Here's how you can do it with Node.js style syntax in a browser.

Events Class:

  • stores callbacks in a hash associated with event keys
  • starts callbacks with parameters provided

To add behavior to your own custom classes, simply extend the Events object (example below).

 class Events { constructor () { this._callbacks = {} } on (key, callback) { // create an empty array for the event key if (this._callbacks[key] === undefined) { this._callbacks[key] = [] } // save the callback in the array for the event key this._callbacks[key].push(callback) } emit (key, ...params) { // if the key exists if (this._callbacks[key] !== undefined) { // iterate through the callbacks for the event key for (let i=0; i<this._callbacks[key].length; i++) { // trigger the callbacks with all provided params this._callbacks[key][i](...params) } } } } // EXAMPLE USAGE class Thing extends Events { constructor () { super() setInterval(() => { this.emit('hello', 'world') }, 1000) } } const thing = new Thing() thing.on('hello', (data) => { console.log('hello ${data}') }) 

Here is a link to github gist with this code: https://gist.github.com/alextaujenis/0dc81cf4d56513657f685a22bf74893d

0
Dec 12 '18 at 22:27
source share

For those who are looking for a simple answer that works. I visited this document only to find out that most browsers do not support it. But at the bottom of the page there was a link to that GitHub page, which basically does what Object.watch () and Object.unwatch () would do, and it works for me!

Here's how you can keep track of changes.

 /* * object.watch polyfill * * 2012-04-03 * * By Eli Grey, http://eligrey.com * Public Domain. * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. * https://gist.github.com/eligrey/384583 */ // object.watch if (!Object.prototype.watch) { Object.defineProperty(Object.prototype, "watch", { enumerable: false , configurable: true , writable: false , value: function (prop, handler) { var oldval = this[prop] , newval = oldval , getter = function () { return newval; } , setter = function (val) { oldval = newval; return newval = handler.call(this, prop, oldval, val); } ; if (delete this[prop]) { // can't watch constants Object.defineProperty(this, prop, { get: getter , set: setter , enumerable: true , configurable: true }); } } }); } // object.unwatch if (!Object.prototype.unwatch) { Object.defineProperty(Object.prototype, "unwatch", { enumerable: false , configurable: true , writable: false , value: function (prop) { var val = this[prop]; delete this[prop]; // remove accessors this[prop] = val; } }); } 

And this should be your code:

 var object = { value: null, changeValue: function(newValue) { this.value = newValue; }, onChange: function(callback) { this.watch('value', function(obj, oldVal, newVal) { // obj will return the object that received a change // oldVal is the old value from the object // newVal is the new value from the object callback(); console.log("Object "+obj+" value got updated from '"+oldValue+"' to '"+newValue+"'"); // object.changeValue("hello world"); // returns "Object object.value value got updated from 'null' to 'hello world'"; // and if you want the function to stop checking for // changes you can always unwatch it with: this.unwatch('value'); // you can retrieve information such as old value, new value // and the object with the .watch() method, learn more here: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/watch }) } }; 

or as short as:

 var object = { user: null }; // add a watch to 'user' value from object object.watch('user', function() { // object user value changed }); 
0
Jul 08 '19 at 17:56
source share



All Articles