Search code examples
javascriptevents

How to use JavaScript EventTarget?


I would like to create a custom event emitter in my client-side programs. I am referencing this (sparse) documentation for EventTarget

My implementation attempt

var Emitter = function Emitter() {
  EventTarget.call(this);
};

Emitter.prototype = Object.create(EventTarget.prototype, {
  constructor: {
    value: Emitter
  }
});

My desired usage

var e = new Emitter();

e.addEventListener("hello", function() {
  console.log("hello there!");
});

e.dispatchEvent(new Event("hello"));
// "hello there!"

Where it fails

var e = new Emitter();
// TypeError: Illegal constructor

What am I doing wrong?


Update

The following is possible, but it's a hack that depends on a dummy DOMElement

var fake = document.createElement("phony");
fake.addEventListener("hello", function() { console.log("hello there!"); });
fake.dispatchEvent(new Event("hello"));
// "hello there!"

I'd like to know how to do this without having to use the dummy element


Solution

  • I gave up on this awhile ago, but recently needed it again. Here's what I ended up using.

    ES6

    class Emitter {
      constructor() {
        var delegate = document.createDocumentFragment();
        [
          'addEventListener',
          'dispatchEvent',
          'removeEventListener'
        ].forEach(f =>
          this[f] = (...xs) => delegate[f](...xs)
        )
      }
    }
    
    // sample class to use Emitter
    class Example extends Emitter {}
    
    // run it
    var e = new Example()
    e.addEventListener('something', event => console.log(event))
    e.dispatchEvent(new Event('something'))


    ES5

    function Emitter() {
      var eventTarget = document.createDocumentFragment()
    
      function delegate(method) {
        this[method] = eventTarget[method].bind(eventTarget)
      }
    
      [
        "addEventListener",
        "dispatchEvent",
        "removeEventListener"
      ].forEach(delegate, this)
    }
    
    // sample class to use it
    function Example() {
      Emitter.call(this)
    }
    
    // run it
    var e = new Example()
    
    e.addEventListener("something", function(event) {
      console.log(event)
    })
    
    e.dispatchEvent(new Event("something"))

    Yeah!


    For those that need to support older versions of ecmascript, here you go

    // IE < 9 compatible
    function Emitter() {
      var eventTarget = document.createDocumentFragment();
    
      function addEventListener(type, listener, useCapture, wantsUntrusted) {
        return eventTarget.addEventListener(type, listener, useCapture, wantsUntrusted);
      }
    
      function dispatchEvent(event) {
        return eventTarget.dispatchEvent(event);
      }
    
      function removeEventListener(type, listener, useCapture) {
        return eventTarget.removeEventListener(type, listener, useCapture);
      }
    
      this.addEventListener = addEventListener;
      this.dispatchEvent = dispatchEvent;
      this.removeEventListener = removeEventListener;
    }
    

    The usage stays the same