JavaScript custom Event Listener

Solution 1:

var evt = document.createEvent("Event");
evt.initEvent("myEvent",true,true);

// custom param
evt.foo = "bar";

//register
document.addEventListener("myEvent",myEventHandler,false);

//invoke
document.dispatchEvent(evt);

Here is the way to do it more locally, pinpointing listeners and publishers: http://www.kaizou.org/2010/03/generating-custom-javascript-events/

Solution 2:

Implementing custom events is not hard. You can implement it in many ways. Lately I'm doing it like this:

/***************************************************************
*
*   Observable
*
***************************************************************/
var Observable;
(Observable = function() {
}).prototype = {
    listen: function(type, method, scope, context) {
        var listeners, handlers;
        if (!(listeners = this.listeners)) {
            listeners = this.listeners = {};
        }
        if (!(handlers = listeners[type])){
            handlers = listeners[type] = [];
        }
        scope = (scope ? scope : window);
        handlers.push({
            method: method,
            scope: scope,
            context: (context ? context : scope)
        });
    },
    fireEvent: function(type, data, context) {
        var listeners, handlers, i, n, handler, scope;
        if (!(listeners = this.listeners)) {
            return;
        }
        if (!(handlers = listeners[type])){
            return;
        }
        for (i = 0, n = handlers.length; i < n; i++){
            handler = handlers[i];
            if (typeof(context)!=="undefined" && context !== handler.context) continue;
            if (handler.method.call(
                handler.scope, this, type, data
            )===false) {
                return false;
            }
        }
        return true;
    }
};

The Observable object can be reused and applied by whatever constructor needs it simply by mixng the prototype of Observable with the protoype of that constructor.

To start listening, you have to register yourself to the observable object, like so:

var obs = new Observable();
obs.listen("myEvent", function(observable, eventType, data){
    //handle myEvent
});

Or if your listener is a method of an object, like so:

obs.listen("myEvent", listener.handler, listener);

Where listener is an instance of an object, which implements the method "handler".

The Observable object can now call its fireEvent method whenever something happens that it wants to communicate to its listeners:

this.fireEvent("myEvent", data);

Where data is some data that the listeners my find interesting. Whatever you put in there is up to you - you know best what your custom event is made up of.

The fireEvent method simply goes through all the listeners that were registered for "myEvent", and calls the registered function. If the function returns false, then that is taken to mean that the event is canceled, and the observable will not call the other listeners. As a result the entire fireEvent method will return fasle too so the observable knows that whatever action it was notifying its listeners of should now be rolled back.

Perhaps this solution doesn't suit everybody, but I;ve had much benefit from this relatively simple piece of code.

Solution 3:

From here:

https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events

// create the event
var evt = document.createEvent('Event');
// define that the event name is `build`
evt.initEvent('build', true, true);

// elem is any element
elem.dispatchEvent(evt);


// later on.. binding to that event
// we'll bind to the document for the event delegation style. 
document.addEventListener('build', function(e){
   // e.target matches the elem from above
}, false);

Solution 4:

Here is a really simple (TypeScript/Babelish) implementation:

const simpleEvent = <T extends Function>(context = null) => {
    let cbs: T[] = [];
    return {
        addListener: (cb: T) => { cbs.push(cb); },
        removeListener: (cb: T) => { let i = cbs.indexOf(cb); cbs.splice(i, Math.max(i, 0)); },
        trigger: (<T> (((...args) => cbs.forEach(cb => cb.apply(context, args))) as any))
    };
};

You use it like this:

let onMyEvent = simpleEvent();
let listener = (test) => { console.log("triggered", test); };
onMyEvent.addListener(listener);
onMyEvent.trigger("hello");
onMyEvent.removeListener(listener);

Or in classes like this

class Example {
    public onMyEvent = simpleEvent(this);
}

If you want plain JavaScript you can transpile it using TypeScript playground.