Is it possible to dispatch events on regular objects (not DOM ones)?

11,110

Solution 1

FileReader has methods like addEventHandler because it is defined to implement the EventTarget interface. EventTarget is defined by the DOM Events spec but you don't need to be a DOM object to implement it. window, XMLHttpRequest and FileReader are other Browser Object Model objects that implement EventTarget.

Unfortunately there's no easy way to piggyback on the browser's native implementation of event targets... you could try inheriting from a browser object by using one as a prototype property, but that's very unreliable in general. However it is not too difficult to write code to implement all the methods yourself in plain JavaScript:

function CustomEventTarget() { this._init(); }

CustomEventTarget.prototype._init= function() {
    this._registrations= {};
};
CustomEventTarget.prototype._getListeners= function(type, useCapture) {
    var captype= (useCapture? '1' : '0')+type;
    if (!(captype in this._registrations))
        this._registrations[captype]= [];
    return this._registrations[captype];
};

CustomEventTarget.prototype.addEventListener= function(type, listener, useCapture) {
    var listeners= this._getListeners(type, useCapture);
    var ix= listeners.indexOf(listener);
    if (ix===-1)
        listeners.push(listener);
};

CustomEventTarget.prototype.removeEventListener= function(type, listener, useCapture) {
    var listeners= this._getListeners(type, useCapture);
    var ix= listeners.indexOf(listener);
    if (ix!==-1)
        listeners.splice(ix, 1);
};

CustomEventTarget.prototype.dispatchEvent= function(evt) {
    var listeners= this._getListeners(evt.type, false).slice();
    for (var i= 0; i<listeners.length; i++)
        listeners[i].call(this, evt);
    return !evt.defaultPrevented;
};

Caution: the above code is off the top of my head and untested, but may work. However it has limitations like only supporting the dispatchEvent return value if the Event object supports the DOM Level 3 defaultPrevented property, and no support for DOM Level 3 stopImmediatePropagation() (which is impossible to implement unless you rely on your own Event object that exposes a property for it). Also there's no implementation of hierarchy or capture/bubbling.

So IMO: you don't gain much by trying to write code that participates in the DOM Events model. For plain-JS callback work I'd just go with your own ad hoc implementation of listener-lists.

Solution 2

bobince has the right idea, but his code is just an example. For an actual battle-tested implementation, Mr. Doob has one that he uses in three.js.

Solution 3

jQuery can dispatch events from regular objects. Here's a fiddle.

function MyClass() {
    $(this).on("MyEvent", function(event) {
        console.log(event);
    });

    $(this).trigger("MyEvent");
}

var instance = new MyClass();
Share:
11,110
jayarjo
Author by

jayarjo

Updated on July 18, 2022

Comments

  • jayarjo
    jayarjo almost 2 years

    I just found out that FileReader dispatches events just as if it was a DOM element. Is it? I wonder if it's possible to create an object similar to FileReader, which doesn't have a representation in HTML/XML structure, but can dispatch events?

  • jayarjo
    jayarjo almost 13 years
    Yes, came here from these pages. I mean that FileReader doesn't seem to be related to any DOM element, or have a reference to it. But at the same time it has a dispatchEvent method in itself and can dispatch events (DOM events I guess). A bit confusing isn't it? I wondered if there was a way to create an object with similar properties.
  • phtrivier
    phtrivier almost 13 years
    Well I don't have any implementation of FileReader around hand, so it's only speculation, but if the DOM element was just kept internally as a private method, and the dispatchEvent of the FileReader was just forwarding the call to the dispatchEvent of the internal DOM element, how would we know ?
  • jayarjo
    jayarjo almost 13 years
    What do you mean by internal DOM element? Is there a real world term like this or are you speculating again? :) I guess if there were any internal DOM elements, then FileReader wouldn't need to forward anything. It could itself be the one.
  • phtrivier
    phtrivier almost 13 years
    Oh sure, I don't know about FileReader, since i guess it's part of the browser it can be implemented any way. I was just thinking : if you wanted to have a custom class of yours with the same interface and apparent behavior, all you would have to do would be to have a private propery of your object be a DOM element (that's what I meant by 'internal'), and use it to dispatch element (minus some potential issues with the currentTarget property of the dispatched event).
  • jayarjo
    jayarjo almost 13 years
    I got my own event model - a function that binds handlers to an "event types", storing them in a hash, and a function that triggers "events" on objects that inherit this model (and some other utility methods). Do you say that default event model doesn't have any valuable benefit over mine?
  • Amit Patil
    Amit Patil almost 13 years
    Yeah, the benefits are quite small I'd say—you can't actually be 100% interoperable with other implementations of Event due to not being able to reliably read whether preventDefault/stopPropagation/stopImmediatePropagation have been called. So the only benefit is the familiarity of the DOM Events interface... but it's arguably quite a clunky interface.
  • jayarjo
    jayarjo almost 13 years
    I've stumbled upon this one: w3.org/TR/DOM-Level-3-Events/#interface-CustomEvent, where W3C recommends it for creating application-specific event types. As far as I see not yet widely supported, but it seems that's where the whole thing is heading.
  • Amit Patil
    Amit Patil almost 13 years
    Yeah,though currently only Opera supports it. And it doesn't really give you that much, only a detail property, which is handy in stricter languages like Java, but of limited use in JS where you can put any arbitrary properties on your objects anyway.
  • bsimpson
    bsimpson about 11 years
    If you aren't as concerned with emulating the ECMAScript API, also consider signals and bacon.
  • harpo
    harpo over 7 years
    Hi @bobince. The difference between native event handlers and this method, which is used by every implementation I've seen (including my own), is that native handlers run in a separate execution context from the dispatcher. Whereas with this method, a failure in one handler will prevent all subsequent handlers from running. This post from 2009 got me thinking about it, although I think the proposed "hybrid" solution may be a bit heavyweight for general use. Thought you'd find it interesting.