Languages
[Edit]
EN

JavaScript - custom event listener class

5 points
Created by:
Dollie-Rutledge
416

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.

Below example provides the following API:

1. Single event listener:

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

var myListener = new SimpleListener();

myListener.add(function(a, b, c, b) { });
myListener.add(function(a, b, c, n) { });

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

2. Composed events listener

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

var myListener = new ComposedListener();

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

myListener.fire('event-name-1', ['a', 'b', 'c', 'n']);
myListener.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;
			var remove = function() {
				if (removed) {
                  	return false;
                }
                removed = true;
                return self.remove(action);
			};
			return remove;
		}
		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 myListener = new SimpleListener();

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

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

2. Complex event listener class example

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 is copied from the first example.
//
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;
			var remove = function() {
				if (removed) {
                  	return false;
                }
                removed = true;
                return self.remove(action);
			};
			return remove;
		}
		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) {
        var entity = listeners[event];
        if (entity == null) {
            var entity = new SimpleListener();
            var remove = entity.add(action); // add method throws exception breaking logic
            listeners[event] = entity
        } else {
          	var remove = entity.add(action)
        }
        count += 1;
      	var proxy = function() {
            if (remove()) {
                count -= 1;
                if (!entity.getSize()) {
                  	delete listeners[event];
                }
                return true;
            }
          	return false;
        };
        return proxy;
	};
	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];
          	entity.clean(); // cleaning because of add method result
          	return true;
        }
	};
	self.fire = function(event, parameters) {
		var entity = listeners[event];
		if (entity) {
			entity.fire(parameters);
        }
	};
  	self.clean = function() {
      	if (count > 0) {
            for (var key in listeners) {
                if (listeners.hasOwnProperty(key)) {
                    var listener = listeners[key];
                    listener.clean(); // cleaning because of add method result
                }
            }
            count = 0;
            listeners = { };
        }
    };
}

// Usage example:

var myListener = new ComposedListener();

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

myListener.fire('event-name-1', ['John', 25, 'London']);
myListener.fire('event-name-2', ['Kate', 21, 'Melbourne']);
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