50

I was wondering if anyone can help me understand how exactly to create different Custom event listeners.

I don't have a specific case of an event but I want to learn just in general how it is done, so I can apply it where it is needed.

What I was looking to do, just incase some folks might need to know, was:

var position = 0;

for(var i = 0; i < 10; i++)
{
    position++;
    if((position + 1) % 4 == 0)
    {
        // do some functions
    }
}
Teun Zengerink
  • 4,277
  • 5
  • 30
  • 32
Lpc_dark
  • 2,834
  • 7
  • 32
  • 49
  • See [this other question](http://stackoverflow.com/questions/2490825/how-to-trigger-event-in-javascript) – bfavaretto Mar 12 '12 at 17:40
  • You can use a library such as jQuery to create custom events and listeners. Have a look at http://fuelyourcoding.com/jquery-custom-events-they-will-rock-your-world/ and http://api.jquery.com/trigger/ – T. Junghans Mar 12 '12 at 17:41
  • 11
    How is the code you posted related to event handling? Do you want to run this code as response to an event? – Felix Kling Mar 12 '12 at 17:42
  • check this [link](http://stackoverflow.com/questions/9670367/javascript-create-class-event/) – Nemoy Mar 12 '12 at 17:51

4 Answers4

71
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/

Max
  • 1,463
  • 5
  • 19
  • 34
  • 11
    **Note:** The `createEvent` method is deprecated. Use [`event constructors`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) instead. – Rahil Wazir Apr 05 '14 at 18:20
  • 4
    Event constructors are not supported in IE. – Nilpo Jan 11 '15 at 00:57
  • Try [dom4](https://github.com/WebReflection/dom4) if you want to use `CustomeEvent` on IE. This is a cross browser _polyfill_ that provides DOM 4 Level 4 interfaces. With this you can use `CustomEvent` on IE 8 or above. – Tsutomu Mar 31 '15 at 00:37
47

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.

Nilpo
  • 4,675
  • 1
  • 25
  • 39
Roland Bouman
  • 31,125
  • 6
  • 66
  • 67
  • Where do you call this.fireEvent("myEvent", data) from? I'm confused about the "this"scope. – perfect_element Aug 11 '13 at 23:35
  • I wrote: "The Observable object can now call its fireEvent method". So, this refers to the instance of the observable object (or an object that mixes in its methods) – Roland Bouman Aug 15 '13 at 08:38
  • Is there any significant difference in behaviour/performance between a solution like the one above and one that creates, adds and despatches custom events on the document DOM object? – Pappa Feb 11 '15 at 17:06
  • @Pappa I don't know. I started doing this to have something that works everywhere. createEvent and CustomEvent constructor aren't supported everywhere. – Roland Bouman Feb 12 '15 at 19:24
14

From here:

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

// create the event
const event = new Event('build');

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


// 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);
Jonathan
  • 8,453
  • 9
  • 51
  • 74
newshorts
  • 1,057
  • 12
  • 12
  • 6
    On the same page https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events#Creating_events it mentions that this is not supported in IE. – phpLearner May 14 '14 at 06:19
9

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.

Ciantic
  • 6,064
  • 4
  • 54
  • 49