Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 219
jayarjo Avatar asked Jul 09 '11 13:07

jayarjo


People also ask

Which property of Dom dispatched an event?

target property is initialized to the current EventTarget . Unlike "native" events, which are fired by the browser and invoke event handlers asynchronously via the event loop, dispatchEvent() invokes event handlers synchronously.

How does dispatch event work?

dispatchEvent method In this example, the event handler is executed as if the click event were generated by user actions. If the event comes from the user actions, the event. isTrusted property is set to true . In case the event is generated by code, the event.

Is dispatchEvent synchronous?

It's guaranteed to be synchronous because: The return value of dispatchEvent indicates whether any of the listeners which handled the event called preventDefault.


2 Answers

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.

like image 127
bobince Avatar answered Oct 13 '22 01:10

bobince


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.

like image 39
bsimpson Avatar answered Oct 13 '22 01:10

bsimpson