0
votes

I have some client side logic (I have a little 3d world where objects interacts), and I would like to add some events listener like this:

 window.addEventListener("myAmazingEvent", () => {doSomethingAmazing})

How can I implement this eventListener to my own class? Like:

    class Person {
       construcor() {

       this.listener = new SomeJavascriptEventListener() 
    }

    crouched() {

       this.listener.call("onCrunch")
    }


const a = new Person()
a.addEventListener("onCrunch", () => {a.startSinging()})

What javascript built in classes can provide this behaviour to me?

1

1 Answers

0
votes

JavaScript's standard library doesn't have any direct event emitter support. There are lots of event emitter libraries you can find with a search, but none of them is built-in.

You can:

  • Use one of the pre-built, pre-tested event emitter libraries that already exist (look for "event emitter" or "publish/subscribe" or "pub/sub").

  • Use something built into the environment you're running your code in. Node.js has an EventEmitter class. On browsers, you could create a DOM element that you keep in your class instance (no need for it to be in the document) and use it to store event listeners, then use dispatchEvent to send events to it. (Though that's not really simpler than the next option.)

  • Build your own by maintaining an array or Set of event listeners for each event type that you loop through and call when you need to emit an event.

All three are viable options.

But to give you an idea, a class with very basic event handling looks like this:

class Example {
    #eventHandlers = {
        "some-event": new Set(),
        "another-event": new Set(),
    };

    on(eventName, handler) {
        const handlers = this.#eventHandlers[eventName];
        if (!handlers) {
            throw new Error(`Invalid event name "${eventName}"`);
        }
        handlers.add(handler);
    }

    off(eventName, handler) {
        const handlers = this.#eventHandlers[eventName];
        if (!handlers) {
            throw new Error(`Invalid event name "${eventName}"`);
        }
        handlers.delete(handler);
    }

    #trigger(eventName, ...eventArgs) {
        const handlers = this.#eventHandlers[eventName];
        // assert(!!handlers);
        for (const handler of handlers) {
            try {
                handler(...eventArgs);
            } catch { // (Add (e) if your environment doesn't support optional catch bindings)
            }
        }
    }

    doSomething() {
        // In this example, "doing something" triggers the `some-event` event
        this.#trigger("some-event", "something happened");
    }
}

const e = new Example();
e.on("some-event", (msg) => console.log(`Handler one: received "${msg}"`));
e.on("some-event", (msg) => console.log(`Handler two: received "${msg}"`));
e.doSomething();

That uses relatively modern things (private fields, optional catch bindings), but you can adapt it as needed.

There are a dozen or more ways to skin this cat. Cross-talk between nandlers, cancelling events, passing in some kind of Event object, etc., etc., etc. But the fundamentals are:

  • Registering handlers (on in the example)
  • Unregistering handlers (off in the example)
  • Triggering events (#trigger in the example)