Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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

share|improve this question
    
Emitter.prototype = Object.create(Emitter.prototype, …) - wait, what? –  Bergi Mar 5 '14 at 2:00
1  
EventTarget is just an interface, not a constructor. Also, you cannot inherit from native DOM structures. –  Bergi Mar 5 '14 at 2:02
    
Bergi, the Object.create line sets up the prototype and the proper constructor. It was an attempt to inherit from the EventTarget "class". –  naomik Mar 5 '14 at 2:08
    
I know what it does. Yet you didn't try to inherit from EventTarget, but from Emitter itself… –  Bergi Mar 5 '14 at 2:12
1  
@Bergi, wups. That was just a typo in the code example here. Fixed. –  naomik Mar 5 '14 at 2:13

3 Answers 3

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.

share|improve this answer
up vote 2 down vote accepted

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

requires ecmascript >= 5


function Emitter() {
  var eventTarget = document.createDocumentFragment();

  function delegate(method) {
    this[method] = eventTarget[method].bind(eventTarget);
  }

  [
    "addEventListener",
    "dispatchEvent",
    "removeEventListener"
  ].forEach(delegate, this);
}

Now a "class" that uses it

function Example() {
  Emitter.call(this);
}

Let's try it out now!

var e = new Example();

e.addEventListener("something", function(event) {
  alert("something happened! check the console too!");
  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

share|improve this answer
    
While you found a solution, it isn't the best way to solve problems like this one. You have a tight-coupling here and that could fail in some browsers. –  DavidY Jun 14 '14 at 4:46
1  
@Scorpion, then please share the best way to solve this problem. I also see no "tight coupling" as Emitter can be used as a standalone or mixed into your other objects as desired. For SO, I'm not going to write a polyfill that works in every browser, but the nice part about Emitter is it gives you a good place to modify methods that need cross-browser compatibility. –  naomik Jun 14 '14 at 5:06
    
@Scorpion, the About Me section on your profile is atrocious. I'm not saying I like it either, but what have you made that has reached the popularity level of the Yii Framework? And who's forcing you to use it? –  naomik Jun 14 '14 at 5:07
    
Yeah my about me section isn't lovely :) The thing that really annoys about Yii is that they claim it to be best of the best. Its full of global state (static classes), it misinterprets MVC, it violates all of the SOLID principles, it does not follow PSR-FIG standards. And the code-base itself is very terrible. And who's forcing me to use that? Well, while I hate Yii, I still can earn good money maintaining another people's code. And yes here's a nice overall explanation for you: stackoverflow.com/a/10960679/1208233 –  DavidY Jun 14 '14 at 15:01

Here is how to do it using CustomEvent, cross-browser (fiddle):

// listen to event
window.addEventListener("say", function(e) { alert(e.detail.word); });

// create and dispatch the event
var event = document.createEvent("CustomEvent");
event.initCustomEvent('say', true, true, 
    { "word": "Hello!" });

window.dispatchEvent(event);

You'd need to use window or document or any other existing DOM element to register listeneres and dispatch the event. EventTarget is not a object, it's an interface. Try accessing EventTarget in JavaScript console and you'll see that.

share|improve this answer
    
MDN documentation specifically says this usage is "old-fashioned" and it kind of sucks that I have to attach to the window (global object). Anyway, I've made an edit to show how you can improve this. Still, it would be nice to encapsulate the events. –  naomik Mar 5 '14 at 2:11
    
@naomik, the edit you made doesn't work in IE9, IE10, IE11. I rolled it back. –  Noseratio Mar 5 '14 at 4:43
    
I think it would've been more constructive to make a note for that solution then. Now we just have a solution using the old Java-influenced API that doesn't even fully solve my problem. And for what it's worth, EventTarget is only documented to support IE >= 9.0, so it's a safe assumption that I'm not looking for an answer that supports an earlier version of IE. –  naomik Mar 5 '14 at 5:34
    
@naomik, it's not about EventTarget, it's about new CustomEvent(...) not working in IE9-IE11, a sad fact. So you have to stick with document.createEvent("CustomEvent"), unless you don't care about IE at all. That's why I rolled back your edit. –  Noseratio Mar 5 '14 at 6:00
    
As to EventTarget, it doesn't exist in IE9-11 either, but that's expected. In the DOM standard, it's defined as interface on a DOM Node object, rather than a standalone object. Although Firefox and Chrome have chosen to implement it as an object, Microsoft may never follow this, as it is a non-standard approach. –  Noseratio Mar 5 '14 at 6:27

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.