|
/******************************************************************* |
|
* 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 |
|
}; |
|
})(); |