Is it possible to dispatch events on regular objects (not DOM ones)?
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();
jayarjo
Updated on July 18, 2022Comments
-
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 almost 13 yearsYes, 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 almost 13 yearsWell 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 almost 13 yearsWhat 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 almost 13 yearsOh 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 almost 13 yearsI 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 almost 13 yearsYeah, 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 whetherpreventDefault
/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 almost 13 yearsI'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 almost 13 yearsYeah,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 about 11 years
-
harpo over 7 yearsHi @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.