Check out "Do you speak JavaScript?" - my latest video course on advanced JavaScript.
Language APIs, Popular Concepts, Design Patterns, Advanced Techniques In the Browser

JavaScript: managing events (dispatch, listen)

As a huge fen of the flash platform, I always like to work with events. The events system in ActionScript3 is really helpful when we are building a big application with many modules and we want to keep them independent. JavaScript hasn't native support of events, but we can still use them.

Events basics

The events are used in the communication between modules of the application. Let's imagine that we have a basic web blog page like this one: [2] And let's say that if you click on contacts we will update the content's area and we will replace the banners' area with our latest articles. Normaly we can use something like:

    menu.contacts.click = function() {
        contentArea.update("contacts data");
        bannersArea.update("latest articles data");
    }
    

That's fine and probably will work for us, but what if you want to add something else. We will have to add additional functions to the code above. Now imagine that we are using classes and we have a complex hierarchy of objects. It is not so easy to have access to these objects in every function. Something, also very important, is that we can't transfer these classes to some other project, because they are tightly connected to each other. By using events you are solving all this problems. The idea is that we have dispatchers (classes that fire events) and listeners (classes that listen for events). [1] So, in the situation above our menu object dispatches an event the contacts button is clicked. The content's area and the banners' area are waiting for this event and they will update themselfs when they catch this event. In other words:

    menu.contacts.dispatch("the contacts button is clicked");
    ...
    ...
    ...
    contentArea.addEventListener("the contacts button is clicked", function() {
        // update code here
    });
    bannersArea.addEventListener("the contacts button is clicked", function() {
        // update code here
    });
    

As you can see, our objects are completely independent. The menu object doesn't care what will happen when the contacts button is clicked. It just informs the other modules for that and that is its job. Also we can easily transfer the menu class to some other project. The only one thing that we have to know is what events will be dispatched.

Managing events (the EventBus class)

Unfortunately JavaScript doesn't have native methods like dispatch and addEventListener. We have to create them manually. Inspired by RobotLegs (AS3 micro-architecture) I created a class called EventBus. It has the following methods: a) addEventListener - adding a listener b) removeEventListener - removing a listener c) dispatch - dispatching event d) getEvents - for debugging purpose, it just print out the added listeners The class is available for download here.

Simple dispatch/listen

We are adding myFunction method as a listener for the my_function_event and after that simply dispatching the event.

    function myFunction(event) {
        alert("myFunction type=" + event.type);
    }
    EventBus.addEventListener("my_function_event", myFunction);
    EventBus.dispatch("my_function_event");
    

Passing the dispatcher

    var TestClass1 = function() {
        this.className = "TestClass1";
        this.callback = function(event) {
            alert(this.className + " = type:" + event.type + " / dispatcher:" + event.target.className);
        }
    };
    var TestClass2 = function() {
        this.className = "TestClass2";
        this.dispatchOurEvent = function() {
            EventBus.dispatch("callback_event", this);
        }
    };
    var t1 = new TestClass1();
    var t2 = new TestClass2();
    EventBus.addEventListener("callback_event", t1.callback, t1);
    t2.dispatchOurEvent();
    

As you can see we have two classes - TestClass1 and TestClass2. Every one of them has a property called className (if you have troubles understanding the classes in javascript please check this article). Of course the property is different for the two classes. What we did above is to add a listener for the event callback_event and then dispatch it. When we use classes it is good to add the scope of our listener function. In our case the function is callback and the scope is t1. The actual version of the addEventListener function is:

    function addEventListener(type, callback, scope) { ... }
    

and for the dispatch method:

    function dispatch(type, target) { ... }
    

If we don't pass the scope we will not have an access to className property. If you test the example above will see that the result is:

    TestClass1 = type:callback_event / dispatcher:TestClass2
    

Something else that is very important to know is that every listener's function receives an object, which has two properties: a) type - the name of the dispatched event b) target - the dispatcher (if is passed)

Passing custom parameters to the listeners function

    var TestClass1 = function() {
        this.className = "TestClass1";
        this.doSomething = function(event, param1, param2) {
            alert(this.className + ".doSomething");
            alert("type=" + event.type);
            alert("params=" + param1 + param2);
            alert("coming from=" + event.target.className);
        }
    };
    var TestClass2 = function() {
        this.className = "TestClass2";
        this.ready = function() {
            EventBus.dispatch("custom_event", this, "javascript events", " are really useful");
        }
    };
    
    var t1 = new TestClass1();
    var t2 = new TestClass2();
    
    EventBus.addEventListener("custom_event", t1.doSomething, t1);
    t2.ready();
    

It is possible to pass as many parameters as you want when you are dispatching an event:

    EventBus.dispatch("custom_event", this, "javascript events", " are really useful");
    

As you can see I passed two strings javascript events and are really useful. The only thing that you should considered is that your parameters are coming after the event object in the listener's function. Get the source code and the example from GitHub.

If you enjoy this post, share it on Twitter, Facebook or LinkedIn.