How can I add event listeners to and dispatch events from my objects of my own classes in JavaScript?
In ActionScript 3 I can simply inherit from Sprite/DisplayObject and use the methods available there. Like this:
// ActionScript-3:
// I can add event listeners to all kinds of events
var mySprite: Sprite = new MySprite();
mySprite.addEventListener("my_menu_item_click", onMenuItemClick);
// later I can dispatch an event from one of my objects
mySprite.dispatchEvent(new Event("my_menu_item_click", ...));
I would like to have the same in JavaScript. Until now I know about window.addEventListener(...)
and document.addEventListener(...)
. I have my own Sprite class in JavaScript so far and I want to use it to dispatch my own events.
// JavaScipt:
function Sprite()
{
this.x = 0;
this.y = 0;
// ...
}
Since both languages seem so "alike" with events I guess I need to inherit from some class? Or do I have to use some of the global variables like window and/or document?
I'm working with HTML5 here. I have only the 1 canvas and a bunch of sprites that are drawn to it and react to user input. I would like one sprite A to subscribe to events of another sprite B. But not to the third sprite C.
How can I add event listeners to and dispatch events from my objects of my own classes in JavaScript?
In ActionScript 3 I can simply inherit from Sprite/DisplayObject and use the methods available there. Like this:
// ActionScript-3:
// I can add event listeners to all kinds of events
var mySprite: Sprite = new MySprite();
mySprite.addEventListener("my_menu_item_click", onMenuItemClick);
// later I can dispatch an event from one of my objects
mySprite.dispatchEvent(new Event("my_menu_item_click", ...));
I would like to have the same in JavaScript. Until now I know about window.addEventListener(...)
and document.addEventListener(...)
. I have my own Sprite class in JavaScript so far and I want to use it to dispatch my own events.
// JavaScipt:
function Sprite()
{
this.x = 0;
this.y = 0;
// ...
}
Since both languages seem so "alike" with events I guess I need to inherit from some class? Or do I have to use some of the global variables like window and/or document?
I'm working with HTML5 here. I have only the 1 canvas and a bunch of sprites that are drawn to it and react to user input. I would like one sprite A to subscribe to events of another sprite B. But not to the third sprite C.
Share Improve this question edited Jan 3, 2021 at 13:14 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked Jun 29, 2015 at 8:07 BitterblueBitterblue 14.1k18 gold badges92 silver badges130 bronze badges 1- Related: stackoverflow./questions/2059456/… – Silviu Burcea Commented Jun 29, 2015 at 8:11
4 Answers
Reset to default 5I tried to add the methods into my Sprite class and succeeded.
Of course not finished yet, but at least it works.
It's what I was looking for.
function Sprite()
{
// ...
this.eventListeners = new Array();
this.addEventListener = function(type, eventHandler)
{
var listener = new Object();
listener.type = type;
listener.eventHandler = eventHandler;
this.eventListeners.push(listener);
}
this.dispatchEvent = function(event)
{
for (var i = 0; i < this.eventListeners.length; i++)
if (event.type == this.eventListeners[i].type)
this.eventListeners[i].eventHandler(event);
}
}
This is some kind of "functional" approach, I don't like using "new" and "this" in javascript. I like plain, peer or extended objects.
// Some helper functions, should be imported from a different file
const partial = fn => (...pargs) => (...args) => fn.apply(null, [...pargs, ...args]);
const partialRight = fn => (...pargs) => (...args) => fn.apply(null, [...args, ...pargs.reverse()]);
// Module starts here
const on = (listeners, type, listener, once) => {
if (!listeners[type]) {
listeners[type] = [];
}
if (listeners[type].indexOf(listener) < 0) {
listener.once = once;
listeners[type].push(listener);
}
};
const once = partialRight(on)(true);
const off = (listeners, type, listener) => {
const listenerType = listeners[type];
if (listenerType && listenerType.length) {
const index = listenerType.indexOf(listener);
if (index !== -1) {
listenerType.splice(index, 1);
}
}
if ((listenerType && !listenerType.length) || !listener) {
delete listeners[type];
}
};
const emit = (listeners, type, ...data) => {
if (listeners[type]) {
listeners[type].forEach(listener => {
listener.apply(null, data);
if (listener.once) {
off(listeners, type, listener);
}
});
}
};
// you could use "export default () => {}" to make it an importable module
const eventEmitter = () => {
const listeners = {};
return {
on: partial(on)(listeners),
once: partial(once)(listeners),
off: partial(off)(listeners),
emit: partial(emit)(listeners),
};
};
const myObj = Object.create(Object.assign({}, eventEmitter()));
myObj.on('hello', name => console.log(name));
setTimeout(() => {
myObj.emit('hello', 'Csaba')
}, 2000)
You can do pretty much the same in JS with the Event and CustomEvent object :
var event = new Event('build');
// Listen for the event.
elem.addEventListener('build', function (e) { ... }, false);
// Dispatch the event.
elem.dispatchEvent(event);
Source : MDN (https://developer.mozilla/en-US/docs/Web/Guide/Events/Creating_and_triggering_events)
You can make your own, this one is generic and very simple. I actually use it myself :D
This is the functionality you'll get.
- addEventListener()
- removeEventlistener()
- dispatchEvent() (Or what ever you wish to call it)
This is the function that will add the needed methods to your object and at the same time return a function that can dispatch events.
// Snippet =========================================
function createEventDispatcher(o) {
var L = o.__listeners = {};
o.addEventListener = function(n, fn) { L[n] = L[n] || []; L[n].push(fn); };
o.removeEventListener = function(n, fn) { var a = L[n]; for (var i = 0; i < a.length; i++) if (a[i] === fn) a.splice(i, 1);};
return function() { var a = Array.prototype.slice.call(arguments); var l = L[a.shift()]; if (l) for (var i = 0; i < l.length; i++) l[i].apply(l[i], a)};
}
:
// Simplified example of usage =========================================
function App(){
// Add functionality
var dispatchEvent = createEventDispatcher(this); // Call snippet
// Use functionality
var count = 0;
setInterval(function(){
dispatchEvent("something",{msg:"hello",count:count++});
},100);
}();
// Somewhere outside your App
function onSomething(event){
console.log(event.msg + "["+event.count+"]");
if(event.count >= 10){
// Remove eventlistener
myApp.removeEventListener("something",onSomething);
}
}
var myApp = new App();
myApp.addEventListener("something",onSomething);
// Easy
https://jsfiddle/squadjot/0n2nby7k/
If you want to remove all listeners from your object, you can simply do something like.
myApp.__listeners = {};