Languages
[Edit]
EN

JavaScript - custom event listener class

5 points
Created by:
Dollie-Rutledge
806

In this article, we're going to have a look at how to write our own event listener class in JavaScript.

Custom event listener class concept implemented JavaScript.
Custom event listener class concept implemented JavaScript.

The motivation to write own class to manage events is lack of support in some cases.

 

The below example provides the following API:

1. Single event listener:

// ------------------------------------------------------------------------
// One listener object per event.

var listener = new SimpleListener();

listener.add(function(a, b, c, b) { /* ... */ });
listener.add(function(a, b, c, n) { /* ... */ });

listener.fire(['a', 'b', 'c', 'n']);

2. Composed events listener

// ------------------------------------------------------------------------
// One listener object per multiple events where actions are grouped by event name.

var listener = new ComposedListener();

listener.add('event-name-1', function(a, b, c, n) { /* ... */ });
listener.add('event-name-1', function(a, b, c, n) { /* ... */ });
listener.add('event-name-2', function(a, b, c, n) { /* ... */ });

listener.fire('event-name-1', ['a', 'b', 'c', 'n']);
listener.fire('event-name-2', ['a', 'b', 'c', 'n']);

Note: go to this article to see TypeScript version.

 

Presented solutions are split into two separated examples:

  • simple event listener - this collection allows to keep many callback functions together and call them together,
  • complex event listener - this collection is similar to the first one with a major difference, the collection groups callback functions into separated events by given event name.

 

1. Simple event listener class example

Presented collection in this section allows to: add, remove, fire, and clean events. add method returns function reference that allows removing added event (just by calling it without any arguments).

// ONLINE-RUNNER:browser;

// This class let's to add functions that are fire method is called.
//
function SimpleListener() {
    var self = this;
    var listeners = [];
    self.getSize = function() {
        return listeners.length;
    };
    self.add = function(action) {
        if (action instanceof Function) {
            listeners.push(action);
            var removed = false;
            return function() {
                if (removed) {
                    return false;
                }
                removed = true;
                return self.remove(action);
            };
        }
        throw new Error('Indicated listener action is not function type.');
    };
    self.remove = function(action) {
        var index = listeners.indexOf(action);
        if (index === -1) {
            return false;
        }
        listeners.splice(index, 1);
        return true;
    };
    self.fire = function(parameters) {
        for (var i = 0; i < listeners.length; ++i) {
            var listener = listeners[i];
            listener.apply(listener, parameters);
        }
    };
    self.clean = function() {
        listeners = [ ];
    };
}


// Usage example:

var listener = new SimpleListener();

listener.add(function(name, age, city) {
	console.log('Function 1: ' + name + ' ' + age + ' ' + city);
});
listener.add(function(name, age, city) {
	console.log('Function 2: ' + name + ' ' + age + ' ' + city);
});
listener.add(function(name, age, city) {
	console.log('Function 3: ' + name + ' ' + age + ' ' + city);
});

listener.fire(['John', 25, 'London']);

 

2. Complex event listener class example

The presented collection in this section provides the same operations as in the first example: add, remove, fire, and clean. The main difference is events grouping - the methods take as the first argument name of the event. Check the below example to know how it works:

// ONLINE-RUNNER:browser;

// This class let's to add functions that are fire method is called.
//
function SimpleListener() {
    var self = this;
    var listeners = [];
    self.getSize = function() {
        return listeners.length;
    };
    self.add = function(action) {
        if (action instanceof Function) {
            listeners.push(action);
            var removed = false;
            return function() {
                if (removed) {
                    return false;
                }
                removed = true;
                return self.remove(action);
            };
        }
        throw new Error( 'Indicated listener action is not function type.' );
    };
    self.remove = function(action) {
        var index = listeners.indexOf(action);
        if (index === -1) {
            return false;
        }
        listeners.splice(index, 1);
        return true;
    };
    self.fire = function(parameters) {
        for (var i = 0; i < listeners.length; ++i) {
            var listener = listeners[i];
            listener.apply(listener, parameters);
        }
    };
    self.clean = function() {
        listeners = [ ];
    };
}

// This class let's to use named events.
//
function ComposedListener() {
    var self = this;
    var count = 0;
    var listeners = {};
    self.getSize = function(event) {
        if (event) {
            var entity = listeners[event];
            if (entity) {
                return entity.getSize();
            }
            return 0;
        }
        return count;
    };
    self.add = function(event, action) {
        if (action instanceof Function) {
            var entity = listeners[event];
            if (entity == null) {
                listeners[event] = entity = new SimpleListener();
            }
            count += 1;
            var remove = entity.add(action);
            return function() {
                if (remove()) {
                    count -= 1;
                    if (!entity.getSize()) {
                        delete listeners[event];
                    }
                    return true;
                }
                return false;
            };
        }
        throw new Error( 'Indicated listener action is not function type.' );
    };
    self.remove = function(event, action) {
        var entity = listeners[event];
        if (entity == null) {
            return false;
        }
        if (action) {
            if (entity.remove(action)) {
                count -= 1;
                if (!entity.getSize()) {
                    delete listeners[event];
                }
                return true;
            }
            return false;
        } else {
            count -= entity.getSize();
            delete listeners[event];
            return true;
        }
    };
    self.fire = function(event, parameters) {
        var entity = listeners[event];
        if (entity) {
            entity.fire(parameters);
        }
    };
    self.clean = function() {
        if (count > 0) {
            count = 0;
            listeners = {};
        }
    };
}


// Usage example:

var listener = new ComposedListener();

listener.add('event-name-1', function(name, age, city) {
	console.log('event-name-1 -> Function 1: ' + name + ' ' + age + ' ' + city);
});
listener.add('event-name-1', function(name, age, city) {
	console.log('event-name-1 -> Function 2: ' + name + ' ' + age + ' ' + city);
});
listener.add('event-name-2', function(name, age, city) {
	console.log('event-name-2 -> Function 3: ' + name + ' ' + age + ' ' + city);
});

listener.fire('event-name-1', ['John', 25, 'London']);
listener.fire('event-name-2', ['Kate', 21, 'Melbourne']);

 

See also

  1. TypeScript - custom event listener class

Alternative titles

  1. JavaScript - how to write own / custom event listener collection class
Donate to Dirask
Our content is created by volunteers - like Wikipedia. If you think, the things we do are good, donate us. Thanks!
Join to our subscribers to be up to date with content, news and offers.
Native Advertising
🚀
Get your tech brand or product in front of software developers.
For more information Contact us
Dirask - we help you to
solve coding problems.
Ask question.

❤️💻 🙂

Join