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


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


Bergi was right about the part, that EventTarget is just an interface and not a constructor.

There are multiple objects in js that are valid event targets. As mentioned there: Element, document, and window are the most common event targets, but there are also others for example Websocket. Anyway, all of them are given.

If you make a short test, you can notice few things:

EventTarget.isPrototypeOf(WebSocket); // true

var div = document.createElement("div");

EventTarget.isPrototypeOf(div.constructor); // true

typeof EventTarget // function

EventTarget() // TypeError: Illegal constructor

EventTarget is prototype of these constructors, which is something you can't set for any other constructor (and even if you could, it wouldnt probably work). Also it is a function, but not callable one.

Now this is the time when you ask: So what is it EventTarget good for and how can I use it?

We have 3 methods that each event emitter needs to implement and there was probably a need to bind these methods together, so we have an interface for them. Which means you can't use EventTarget for calling purposes, but some other native functions might. This is similar like creating elements, we have document.createElement factory method and we don't (can't) use new HTMLDivElement() to create a new element, but we can compare constructors of two elements.

Conclusion

If you want to create custom event emitter, you always have to create some dummy object or use some that already exists. From my point of view, it doesn't matter what object it will be.

Some methods are not callable, but still can be compared as properties of objects. Therefore they are visible. EventTarget is one of them.