4

How to use EventTarget as a web-native event emitter

 1 year ago
source link: https://www.stefanjudis.com/today-i-learned/how-to-use-eventtarget-as-a-web-native-event-emitter/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How to use EventTarget as a web-native event emitter

This post is part of my Today I learned series in which I share all my web development learnings.

Suppose you want to implement a publish/subscribe pattern in your Frontend application to react to data changes and events. First, you might be looking for an event emitter library.

I installed plenty of event emitter libraries over the years, and if I felt advanterous I wrote them from scratch as a quick coding exercise.

Today I learned that vanilla JavaScript comes with a native event emitter, which I've been indirectly using forever. There's no need for extra code!

The EventTarget interface

It's right in front of us; every time you use addEventListener on a DOM element, you subscribe to events from it. That's a classical event emitter. Can you reuse this functionality somehow?

I've always thought that addEventListener is part of the HTMLElement's prototype, but surprisingly it's not!

Where's the method coming from?

If you inspect the HTMLElement prototype chain, you'll discover it inherits from Element, Node and EventTarget.

Prototype chain of HTMLElement inheriting from Element, Node and EventTarget.

Thanks to the EventTarget interface, you can subscribe to an element's DOM events via addEventListener. MDN defines EventTarget as follows:

The EventTarget interface is implemented by objects that can receive events and may have listeners for them. In other words, any target of events implements the three methods associated with this interface.

Any object inheriting from EventTarget becomes an event emitter!

Extending EventTarget to create an event emitter

Let's initialize a new EventTarget and see what we get.

const emitter = new EventTarget()

typeof emitter.addEventListener    // "function"
typeof emitter.removeEventListener // "function" 
typeof emitter.dispatchEvent       // "function"

emitter.addEventListener('YOLO', () => {
  console.log('YOLO');
})

// invoke attached event listeners
emitter.dispatchEvent(new Event('YOLO'));

Using the bare-bones EventTarget is handy for simple logic, but if you need to do more than sending events, you probably want to pair it with custom logic.

For example, to keep your views up to date, you might want to store state in an event emitter, alter this state and rerender views whenever data changes.

To achieve this, use the JS class syntax, extend the EventTarget interface and pair it with your application logic.

Find a simple "store" event emitter below that holds data and informs listeners when its state changes.

// Extend the `EventTarget` class to get all the goodies
export class MyEventEmitter extends EventTarget {
  #list;

  constructor(list = []) {
    super();
    this.#list = list;
  }

  getItems() {
    return this.#list;
  }

  addItem(item) {
    this.#list = [...this.#list, item];
    // Dispatch a new event to notify listeners
    this.dispatchEvent(new Event("update"));
  }
}

EventTarget is pretty cool stuff! I wonder what other interface gems are hidden in our browers. 🤔


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK