dholm
dholm

Reputation: 23

How do you do custom events on a JavaScript (ES6) class?

When I search for custom events on JavaScript classes, I get a lot of old or incomplete results. MDN shows a custom event off of a dom element. Stackoverflow's top result is almost ten years old and it uses the pre-ES6 syntax.

Is there a way of doing something like:

class Dog
{
  constructor(name)
  {
    this.name = name;
  }
  
  //something to expose bark event
  
}

const buddy = new Dog('buddy');
buddy.addEventListener("bark", function(e) {
  console.log(`${this.name} barked!`);
});

Upvotes: 2

Views: 3614

Answers (4)

programmingisphun
programmingisphun

Reputation: 130

@varaprasadh approach helped me get started, but lacked accumulating multiple events. Modified to also allow remove listener that functions similar to on DOM events to get rid of one based on callback.

class Dog {
  constructor() {
    this.listeners = {};
  }

  emit(method, payload = null) {
    if (this.listeners.hasOwnProperty(method)) {
      const callbacks = this.listeners[method];
      for (let [key, callback] of Object.entries(callbacks)) {
        if (typeof callback === 'function') {
          callback(payload);
        }
      }
    }
  }

  addEventListener(method, callback) {
    if (!this.listeners.hasOwnProperty(method)) {
      this.listeners[method] = {}
    }
    this.listeners[method][callback] = callback;
  }

  removeEventListener(method, callback) {
    if (this.listeners.hasOwnProperty(method)) {
      delete this.listeners[method][callback];
    }
  }
}

Upvotes: 0

Fernando Bonafé
Fernando Bonafé

Reputation: 141

Extend EventTarget class to get all event functions:

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/EventTarget

Upvotes: 1

varaprasadh
varaprasadh

Reputation: 505

But in your snippet, you wanted to have dog.bark(), in that case see below

class Dog {
  addEventListener(method,callback) {
     this[method] = callback;
  }

  removeEventListener (method) {
      delete this[method];
   }
}

The above will work as

const dog = new Dog();

dog.addEventListener('bark', ()=>console.log("bow"));
dog.bark() // logs => bow

dog.addEventListener('somethingsomething', ()=>{ /*do something*/ })

dog.removeListener('bark');

We can implement tiny class as EventEmitter pattern

class Dog {
   constructor() {
      this.listeners = {};
   }

   emit(method, payload = null) {
      const callback = this.listeners[method];
      if(typeof callback === 'function'){
          callback(payload);
      }
  }

  addEventListener(method,callback) {
     this.listeners[method] = callback;
  }

  removeEventListener (method) {
      delete this.listeners[method];
   }
}

And we can use this class like this

const dog = new Dog();

dog.addEventListener('bark',(customSound)=>console.log(customSound || "Bow Bow"));

dog.addEventListener('eat', ()=>console.log("eating yum yum") );

dog.emit('bark') // logs => Bow Bow
dog.emit('bark', 'i can talk humans') // logs => i can talk humans
dog.emit('eat');

dog.removeEventListener('bark');

Note: it's raw implementation, not production ready code. Thanks.

Upvotes: 4

epascarello
epascarello

Reputation: 207547

There are no events with a class. You can implement something that registers functions and you can call them when the method is triggered. Basic idea:

class Animal {
  #registered = {};
  
  constructor(name)
  {
    this.name = name;
  }  
  
  addEventListener(name, callback) {
    if (!this.#registered[name]) this.#registered[name] = [];
    this.#registered[name].push(callback);
  }
  triggerEvent(name, args) {
     this.#registered[name]?.forEach(fnc => fnc.apply(this, args));
  }
}


class Dog extends Animal
{
  constructor(name)
  {
    super(name);
  }
  
  bark(){
    console.log('bark was called');
    this.triggerEvent('bark');
  }

  eat(what){
    console.log('eat was called', what);
    this.triggerEvent('eat', [what]);
  }

}

const buddy = new Dog('buddy');
buddy.addEventListener("bark", function() {
  console.log(`${this.name} barked!`);
});
buddy.addEventListener("eat", function(item) {
  console.log(`${this.name} ate a ${item}!`);
});
buddy.bark();
buddy.eat('bone');

Upvotes: 2

Related Questions