TIL – addEventListener accepts functions and (!) objects

To build interactive web interfaces, you have to use DOM (Document Object Model) events. How does that usually work?

You define the event type you're interested in, pair it with a callback function and you are ready to react to clicks, keypresses, scrolls and many other events.

For example, to react to a button click, the following code can be used:

document.querySelector('button')
  .addEventListener('click', () => {
    console.log('element clicked');
  });

The code queries the DOM, grabs a specific element and adds a click event listener to it using addEventListener.

According to MDN, target.addEventListener defines the following parameters:

target.addEventListener(type, listener [, options]);
target.addEventListener(type, listener [, useCapture]);
target.addEventListener(type, listener [, useCapture, wantsUntrusted  ]); // Gecko/Mozilla only

addEventListener accepts the event type, a listener callback function and an options or useCapture parameter.

(To learn more about possible options or useCapture head over to the MDN addEventListener documentation.)

What if I told you, that the listener parameter can be a function but also an object?

addEventListener and the EventListener interface

It turns out that MDN defines listener as the following:

[listener] must be an object implementing the EventListener interface, or a JavaScript function.

The early DOM events specification (we're talking pre-HTML5 here) defined an EventListener interface. Objects implementing the interface (they had to define a handleEvent method) where valid to be passed to the addEventListener method.

// a class implementing
// the `EventListener` interface
class EventHandler {
  constructor() {
    this.eventCount = 0;
  }

  handleEvent() {
    this.eventCount++;
    console.log(`Event triggered ${this.eventCount} time(s)`);
  }
}

document.querySelector('button')
  .addEventListener('click', new EventHandler());

The code above uses a JavaScript class EventHandler. Initialized event handler objects can be passed to addEventHandler and they keep track of the number of times a specific event occurred (check it on CodePen). All information is stored in the object itself, and the code works without any out of scope variables. I like this pattern and I can see it come in handy when dealing with sequential events.

According to MDN, the EventListener interface is supported by all major browsers and you can safely pass objects implementing it to addEventListener.

When would you pass EventListener objects to addEventListener? I'd love to learn about more examples!