JavaScript - custom event listener class
In this article, we're going to have a look at how to write our own event listener class in 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 - actions 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']);