Click here to Skip to main content
15,891,888 members
Articles / Web Development / HTML

JavaScript Global Event System

Rate me:
Please Sign up or sign in to vote.
4.00/5 (2 votes)
19 Aug 2016CPOL6 min read 14.6K   66   2   2
An article about how to create and use a global event system for disconnected modules

Introduction

This article is about the use of global event system in the JavaScript language. In any application which is modular in nature, there are situations when we need the modules to communicate with each other which are disconnected in nature. The modules could be anything from just a simple collection of functions to full fledged UI controls.

Whatever the case may be, whenever we are required to execute a piece of code inside a module from another module but we also have to maintain the integrity of the application so that the modules will not communicate with each other directly, we need to use an intermediate component; the most usual choice is a Global Event System.

An event system could be used to register events and their handlers. The events could be wired up at the time of component or module initialization and could be removed when the module is disposed and no longer in use.

Global event systems make even more sense when they are used with modules having their own life cycle events like ASP.NET's Ajax controls or ReactJS components. We can pinpoint the places where we need to add and remove global events and the end result will be like our expectations.

Background

Event systems - both local and global have been in use for quite a long time. With the quick emergence of React like code and view libraries, the inexperienced developers often times feel it appropriate to have the components communicate with each other but are unable to come up with a suitable solution to this problem.

Using the Code

A global event system works by acting between two disconnected modules. The module which needs to handle another module's state change adds a new event handler to the event system. When the state of the first module changes, then it can choose to raise a global event which could be handled by multiple dependent modules.

The following is a very simple scenario of two disconnected modules and a very basic event system code which could be used by the modules to communicate with each other.

In the next code sections, there are two HTML elements; one is a text input and the other one is a button. We have to disable the button based on the length of the text value in the input element. Both the input and button have been associated with a code module to handle their various functions.

The input module validates the input value and raises the global event to notify the button module that the validation result has changed after the last input change. The button module then handles the event and changes the enabled state of the button element accordingly.

So if the input is empty, the button will remain disabled, else the button will be enabled.

Now let's see the code and description of each file. Every module and the event library code will be in its own js file. At the time of building the application, the code could be concatenated and minified using Gulp, Grunt or any other tool.

Main HTML Page (index.html)

HTML
<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <input id="txtName" value="John Doe"/> <br /><br />
    <button id="btnSubmit">Submit</button>

    <script src="js\app\app.js"></script>
    <script src="js\control-modules\button-module.js"></script>
    <script src="js\control-modules\input-module.js"></script>
    <script src="js\lib\event-system.js"></script>
</body>
</html>

Description: This is the main HTML page which contains the inputs and the script references. The app.js will initialize everything and we don't have to write any other code over here.

Application Module (app.js)

JavaScript
;(function (w, d) {
    w.App = w.App || {};
    window.addEventListener('DOMContentLoaded', function () {

        //create the input module
        var txtName = new App.InputModule(d.getElementById('txtName'));
        txtName.setValidation('Required', 'change', function () {
            var args = {};
            args.IsValid = this.control.value.length;
            App.EventSystem.RaiseEvent('SetButtonState', args);
        })

        //create the button module
        var btnSubmit = new App.ButtonModule(d.getElementById('btnSubmit'))
    });
})(window, document);

Description: The code over here will initialize the modules associated with the HTML elements. There is a validation being added for the required value of the input element. The function which would be called whenever the input is changed will raise a global event which has to be handled by every module which intends to do something when the required field validation is finished. The validation result is being passed as an argument to the SetButtonState event while it is being raised.

Event System Object (event-system.js)

JavaScript
;(function (w) {
    w.App = w.App || {};

    var eventSystem = {

        'AddEvent': function (name, handler) {
            (this.Events[name])
            ? this.Events[name].Handlers.push(handler)
            : this.Events[name] = { Handlers: [handler] };            
        },

        'RaiseEvent': function (name, args) {
            var i = 0;
            if (this.Events[name]) {
                for(;i<this.Events[name].Handlers.length;i++) {
                    this.Events[name].Handlers[i].call(null, args);
                }
            }
        },

        'RemoveEvent': function (name, handler) {
            var idx = this.Events[name].Handlers.indexOf(hander);
            if (idx > -1) {
                this.Events[name].Handlers.splice(idx, 1);
            }            
        },

        'Events': {}
    };

    w.App.EventSystem = eventSystem;
})(window);

Description: This is the global event system object which could be accessed using the main App namespace. In your application, you might decide to plant this object further down into your application namespace chain based on the naming convention that you are using.

This event object can have numerous methods but at the core, there are methods to add, raise and remove an event. More functions could be created to augment these core methods.

The AddEvent function will simply add a new event to the event object's Events collection. The Events collection is also an object and that is because it is easier to access the events when they are saved as individual properties.

Each event is an object and it contains a Handler list which will contain all the handlers added to a single event. When any event is raised, then all the handlers are executed one by one and the arguments are also passed along with calling the handler function.

RemoveEvent will simply remove the specific event handler leaving the rest of the handlers intact. This event object is very basic it might need more testing and tweaking before it could be used in a production setting.

Input Module (input-module.js)

JavaScript
;(function (w) {
    w.App = w.App || {};
    var inputModule = function (c) {
        this.control = c || null;
        this.Validations = [];

        //initialize the control
        init();

        this.setValidation = function (name, event, func) {
            var handler = (func).bind(this);
            this.control.addEventListener(event, handler);
            this.Validations.push({ 'Name': name, 'Handler': handler });
        }

        function init() {

        };
    };

    w.App.InputModule = inputModule;
})(window);

Description: This module is for all the input HTML elements. There is a setValidation function which could be used to call a specific function when any input event is raised. The exact validation handler needs to be implemented when we are initializing the module. You could also add pre-defined validations as per your requirements as I have only coded the necessary bit to show how the global event system is being used over here.

Button Module (button-module.js)

JavaScript
;(function (w) {
    w.App = w.App || {};
    var buttonModule = function (c) {
        this.control = c || null;

        //initialize the control
        init();

        function init() {
            //add the global event here
            App.EventSystem.AddEvent('SetButtonState', eventSetState)
        };

        var setState = (function (d) {
            this.control.disabled = d;
        }).bind(this);

        function eventSetState(args) {
            setState(!args.IsValid);
        }
    };

    w.App.ButtonModule = buttonModule;
})(window);

Description: This is the module for a button element. In this code, there is an init() function which gets called as soon as the module object is initialized using its constructor function. Inside the init() function, the event SetButtonState is being added to the global event collection. This same event is also getting raised from the input element module's required validation handler function which we saw previously.

The function setState which is being assigned as the event handler is simply setting the enable state of the button depending on the validation result.

You might have also noticed that the setState function is created using function.Bind; this is important because if we want to use the 'this' keyword to access the control reference as well as other functions, we need to set a fixed scope of the handler function irrespective of where it is being called from.

It's time to run the application in the attached code. I would suggest to place breakpoints at several places to see how the code execution jumps from module to event object and then back to another module when any global event is raised.

Points of Interest

A global event system is a must when we have disconnected components or modules in our application. I would suggest not to be shy in using it and take leverage of this powerful design pattern which is very useful in the JavaScript language more than any other language that I could think of.

History

  • 19th August, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
India India
Just a regular guy interesting in programming, gaming and a lot of other stuff Smile | :)

Please take a moment to visit my YouTube Channel and subscribe to it if you like its contents!
My YouTube Channel

Don't be a stranger! Say Hi!!

Cheers!

Comments and Discussions

 
QuestionWhats the comma at the beginning every IIFE for? Pin
Paul Tait19-Aug-16 19:42
Paul Tait19-Aug-16 19:42 
AnswerRe: Whats the comma at the beginning every IIFE for? Pin
Nitij20-Aug-16 3:35
professionalNitij20-Aug-16 3:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.