Skip to content

Instantly share code, notes, and snippets.

@zakirt
Last active July 16, 2018 10:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save zakirt/8176061 to your computer and use it in GitHub Desktop.
Save zakirt/8176061 to your computer and use it in GitHub Desktop.
Event - JavaScript module handling addition/removal of multiple events

JavaScript Event Handling Module

A JavaScript module allowing addition and removal of multiple events and callback handlers. Its most useful feature is the ability to keep track of attached event handlers. Multiple event handlers can be attached and removed to/from a specific event. The module allows removal of all event listeners at the same time for a specific event, or all listeners for all events, if the user so desires.

  • Can be used with collections, or with a single DOM object.
  • Works on older browsers.

This module was inspired by this answer at Stack Overflow: http://stackoverflow.com/a/4386514

A simple example using Event module can be seen here: http://codepen.io/zakirt/pen/tjkEg

A Pen by Zakir Tariverdiev on CodePen.

License.

/*******************************************************************
* Name: Event
* Author: Zakir Tariverdiev
* Desc: A cross-browser event listening module.
* This module was inspired by this answer at
* Stack Overflow:
* http://stackoverflow.com/a/4386514
* The module works with either a collection or a single
* DOM element. The module keeps track of DOM elements
* by storing them in a hashmap and references the elements
* by their unique ID.
* Gist: https://gist.github.com/zakirt/8176061
* Codepen: http://codepen.io/zakirt/pen/hxDrF
********************************************************************/
var Event = (function() {
// --------------------------------------
// PRIVATE
// --------------------------------------
// To store events along with their handlers
var eventHandlers = {};
// Unique IDs we'll use to reference DOM elements
var idCounter = new Date().getTime();
// Prefix for IDs generated by Event module
var idPrefix = 'Event_domElemId';
// Use lazy initialization to create a
// cross-browser add/remove listener methods.
var _addEventListener;
var _removeEventListener;
// Add event listener
// Modern browsers
if (typeof window.addEventListener !== 'undefined') {
_addEventListener = function(elem, event, callback, capture) {
elem.addEventListener(event, callback, capture);
};
}
// IE < 9
else if (typeof window.attachEvent !== 'undefined') {
_addEventListener = function(elem, event, callback) {
elem.attachEvent('on' + event, callback);
};
}
// Very old browsers
else {
_addEventListener = function(elem, event, callback) {
elem['on' + event] = callback;
};
}
// Remove event listener
// Modern browsers
if (typeof window.removeEventListener !== 'undefined') {
_removeEventListener = function(elem, event, callback, capture) {
elem.removeEventListener(event, callback, capture);
};
}
// IE < 9
else if (typeof window.detachEvent !== 'undefined') {
_removeEventListener = function(elem, event, callback) {
elem.detachEvent('on' + event, callback);
};
}
// Very old browsers
else {
_removeEventListener = function(elem, event) {
elem['on' + event] = null;
};
}
// --------------------------------------
// The following methods will have a
// public interface available for them.
// --------------------------------------
/**
* Method: _addListener(elem, event, callback, capture)
* Desc: Attaches the specified event with
* callback to the DOM object (elem).
* Param: object elem - DOM element
* Param: String event - click, mouseover, etc...
* Param: function callback - event handler
* Param: Boolean capture (optional)
**/
var _addListener = function(elem, event, callback, capture) {
capture = capture || false;
// Collection?
if (elem.length) {
for (var i = 0, len = elem.length; i < len; i += 1) {
_createListener(elem[i], event, callback, capture);
}
}
// Nope, just a single DOM element
else {
_createListener(elem, event, callback, capture);
}
};
/**
* Method: _removeListener(elem, event, callback)
* Desc: Detaches specified event handler from the
* DOM object (elem). If handler is not
* speficied, all handlers wil be removed.
* If the event is not specified, then all
* listeners (for all events) will be removed.
* Param: object elem
* Param: String event (optional)
* Param: function callback (optional).
**/
var _removeListener = function(elem, event, callback) {
// Removing a collection of DOM elements?
if (elem.length) {
for (var i = 0, len = elem.length; i < len; i += 1) {
_deleteListener(elem[i], event, callback);
}
}
// Nope, just a single DOM element
else {
_deleteListener(elem, event, callback);
}
};
/**
* Method: _getEvent
* Desc: Use it in the event handler functions to
* get the DOM event object across all browsers.
* Param: event - either DOM event, or undefined (IE).
* Returns: object event
**/
var _getEvent = function(event) {
return event || window.event;
};
/**
* Method: _getTarget
* Desc: Use this to get the target object across all browsers.
* Param: object event - either DOM event, or undefined (IE).
* Returns: target object, or srcElement (IE).
*/
var _getTarget = function(event) {
var evt = _getEvent(event);
return (typeof evt.target !== 'undefined') ? evt.target : evt.srcElement;
};
// --------------------------------------
// The following are private utility
// methods that do not have a public
// interface available.
// --------------------------------------
/**
* Method: _createListener(elem, event, callback, capture)
* Desc: Places the DOM element's ID along with event handler
* inside eventHandlers hahmap. Used primarily by
* addListsner method to deal with collections vs.
* a single DOM element.
* Param: object elem - DOM element
* Param: String event
* Param: function callback
* Param: Boolean capture
**/
var _createListener = function(elem, event, callback, capture) {
if (!(elem.id in eventHandlers)) {
elem.id = _getId(elem);
eventHandlers[elem.id] = {};
}
var elemId = elem.id;
if (!(event in eventHandlers[elemId])) {
eventHandlers[elemId][event] = {};
}
if (!(callback in eventHandlers[elemId][event])) {
eventHandlers[elemId][event][callback] = [callback, capture];
}
// Cross-browser listener for the specified event
_addEventListener(elem, event, callback, capture);
};
/**
* Method: _deleteListener(elem, event, callback)
* Desc: Removes the listsner info from the eventHandlers hashmap.
* Used primarily by the removeListener method.
* Param: object elem - DOM element
* Param: String event
* Param: function callback
**/
var _deleteListener = function(elem, event, callback) {
var elemId = elem.id;
if (elemId in eventHandlers) {
// Remove the specified event
if (typeof event !== 'undefined' &&
event in eventHandlers[elemId]) {
// Remove specified event handler
if (typeof callback !== 'undefined') {
_removeHandler(elem, event, callback);
}
// Remove all handlers for this event
else {
_removeAllHandlers(elem, event);
}
}
// Remove all listeners for the element
else {
_removeAllListeners(elem);
}
}
};
/**
* Method: _removeHandler(elemId, event, callback)
* Desc: Removes the callback handler for the
* specified event.
* Param: object elem - DOM element
* Param: String event - click, mouseover, etc...
* Param: function callback - event handler
**/
var _removeHandler = function(elem, event, callback) {
var elemId = elem.id;
var capture = eventHandlers[elemId][event][callback][1];
_removeEventListener(elem, event, callback, capture);
if (typeof eventHandlers[elemId][event][callback] !== 'undefined') {
delete eventHandlers[elemId][event][callback];
}
// No more handlers attached?
if (_isEmpty(eventHandlers[elemId][event])) {
// No, so detach the event from the element
delete eventHandlers[elemId][event];
// No more events exist for this element?
if (_isEmpty(eventHandlers[elemId])) {
// No, so remove it from memory
delete eventHandlers[elemId];
}
}
};
/**
* Method: _removeAllHandlers(elem, event)
* Desc: Removes all callback handlers from a
* specified event.
* Params: DOM object elem, String event
**/
var _removeAllHandlers = function(elem, event) {
var handlers = eventHandlers[elem.id][event];
for (var i in handlers) {
var handler = handlers[i][0];
_removeHandler(elem, event, handler);
}
};
/**
* Method: _removeHandler(elem)
* Desc: Removes all listeners for all events
* created with Event module for the
* specified DOM object (elem).
* Param: object elem - DOM element
**/
var _removeAllListeners = function(elem) {
var elemId = elem.id;
for (var event in eventHandlers[elemId]) {
_removeAllHandlers(elem, event);
}
};
/**
* Method: _isEmpty(obj)
* Desc: Checks if the object has no properties.
* Param: object obj
* Returns: Boolean true/false
**/
var _isEmpty = function(obj) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
return false;
}
}
return true;
};
/**
* Method: _getId(elem)
* Desc: Retrieve DOM element's unique ID.
* If it doesn't have one,
* then generate one for it.
* Param: object elem - DOM element
* Returns: String with DOM element's ID
**/
var _getId = function(elem) {
// No ID was set for the DOM element,
// so we'll add our own unique ID.
if (!elem.id) {
// We need to make sure that this ID really is unique
while (document.getElementById(idPrefix + idCounter)) {
idCounter += 1;
}
elem.id = idPrefix + idCounter++;
}
return elem.id;
};
// --------------------------------------
// PUBLIC
// --------------------------------------
return {
addListener: _addListener,
removeListener: _removeListener,
getEvent: _getEvent,
getTarget: _getTarget
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment