How to use EventTarget as a web-native event emitter
- Published at
- Updated at
- Reading time
- 2min
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!
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
.
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!
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. 🤔
Sam Thorogood published an in-depth article on EventTarget
, if you want to learn more about it.
Join 5.5k readers and learn something new every week with Web Weekly.