2
\$\begingroup\$

For many years, there have been several JavaScript libraries for implementing custom events and event listeners. But, as MDN web docs suggests, there are now available constructable Event, CustomEvent and EventTarget objects. So, what would now be the standard approach to equip my custom class with event-emitting qualities?

This is the ES6 code I am using now, it works fine.

class MyObject extends EventTarget {
  constructor(name) {
    super();
    this.name = name;
  }
  trigger(message) {
    const e = new CustomEvent('update', {detail: {message}});
    this.dispatchEvent(e);
  }
}

const o = new MyObject('Good Boy');

o.addEventListener('update', e => {
  console.log('Update event by', e.target.name, 'triggered, message:', e.detail.message);
});

// Should output to the console: Update event by Good Boy triggered, message: Hello
o.trigger('Hello');

Are there any risks with this approach? Other than that it would not work on certain older browser versions. I have some reservations about the fact that I have to inherit from the EventTarget class. If the MyObject class would inherit from some parent class, and I'd want to equip with event emitter only the child class, it appears there would be no clean way to do that.

Here's my lousy attempt to mix in the EventTarget methods so that there's no need to inherit from the EventTarget class:

class MyObject {
  constructor(name) {
    this.name = name;
    this.eventEmitter = new EventTarget();
    this.addEventListener = (...args) => this.eventEmitter.addEventListener(...args);
    this.removeEventListener = (...args) => this.eventEmitter.removeEventListener(...args);
    this.dispatchEvent = (...args) => this.eventEmitter.dispatchEvent(...args);
  }
  trigger(message) {
    const e = new CustomEvent('update', {detail: {emitter:this, message}});
    this.dispatchEvent(e);
  }
}

const o = new MyObject('Good Boy');

o.addEventListener('update', e => {
  console.log('Update event by', e.detail.emitter.name, 'triggered, message:', e.detail.message);
});

o.trigger('Hello');

The ugly thing here is that now e.target does not point to MyObject instance but rather to its eventEmitter property. I am forced to pass it via the event details, it's not pretty but this kind of works.

\$\endgroup\$
4
  • \$\begingroup\$ What do you want to use the Events for? You are using DOM Events, thus you'd usually . dispatch() and .addEventListener() on built-in DOM Elements or on CustomElements. Both have those methods built-in and you'd built wrappers around the actual CustomEvents instead. \$\endgroup\$ – Christian Ulbrich Jan 12 '20 at 23:33
  • \$\begingroup\$ In this case, it's an abstract application business logic model with no visual representation. It would emit events when it changes state. Of course, I can implement my own code for addEventListener and removeEventListener methods and handle the event callback. But why bother writing code for something that's available out-of-the-box? \$\endgroup\$ – Passiday Jan 14 '20 at 16:02
  • \$\begingroup\$ Instead of abusing DOM Events for an offscreen event system that is not using the DOM specific semantics that are associated with DOM events (like bubbling) - I'd rather use an existing solution (e.g postal.js) or come up with a specific customized solution. Or to put it simply: I'd not fix a problem with a hammer, that is lying around if a screw would be a better fit. \$\endgroup\$ – Christian Ulbrich Jan 14 '20 at 16:34
  • \$\begingroup\$ Oh, Ok. I wasn't aware those classes are meant for the DOM element business only. If so, that really is a hack to use the Event on non-elements. It was so satisfactory to see an inheritable Event class being available in the browser environment, event system is no less core to the application develompent than things like Promise. \$\endgroup\$ – Passiday Jan 14 '20 at 21:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.