Last active
March 19, 2018 16:27
-
-
Save fabiocicerchia/7116129 to your computer and use it in GitHub Desktop.
JavaScript - Get events bound to DOM elements
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Event Container Class | |
*/ | |
var EventContainer = function () { | |
/** | |
* The Event Container. | |
* | |
* @property container | |
* @type {Object} | |
* @default {} | |
*/ | |
this.container = {}; | |
/** | |
* Current instance. | |
* | |
* @property currentEventContainer | |
* @type {Object} | |
* @default this | |
*/ | |
var currentEventContainer = this; | |
/** | |
* Hash a string with SHA1 (if exists). | |
* | |
* @method hashString | |
* @param {String} string The string to be converted to hash | |
* @return {String} | |
*/ | |
this.hashString = function (string) { | |
if (typeof SHA1 !== 'undefined') { | |
return SHA1(string); | |
} else { | |
return string.replace(/[^a-zA-Z0-9]/g, '_'); | |
} | |
}; | |
/** | |
* Returns the XPath for a certain DOM element. | |
* | |
* @method getXPath | |
* @param {DOMElement} element The DOM element to be converted as XPath | |
* @return {String} | |
*/ | |
this.getXPath = function (element) { | |
var xpath = '', | |
nodeList, | |
id; | |
for (; element && element.nodeType == 1; element = element.parentNode) { | |
nodeList = Array.prototype.slice.call( | |
element.parentNode.getElementsByTagName(element.tagName) | |
); | |
id = nodeList.indexOf(element) + 1; | |
id = id > 1 ? ('[' + id + ']') : ''; | |
xpath = '/' + element.tagName.toLowerCase() + id + xpath; | |
} | |
return xpath; | |
}; | |
/** | |
* Retrieve a list of DOM elements based on their attributes. | |
* | |
* @method getElementsByAttribute | |
* @param {DOMElement} element The root element. | |
* @param {String} attr The attribute name ('*' is a wildcard). | |
* @return {Array} | |
*/ | |
this.getElementsByAttribute = function (element, attribute) { | |
var arr_elms = element.getElementsByTagName('*'); | |
var elements = []; | |
var wildcard = attribute.substr(-1, 1) === '*'; | |
var curr_attr; | |
if (wildcard) { | |
attribute = attribute.substr(0, attribute.length - 1); | |
} | |
for (var i = 0; i < arr_elms.length; i++) { | |
for (var j = 0, attrs = arr_elms[i].attributes, l = attrs.length; j < l; j++){ | |
curr_attr = attrs.item(j).nodeName; | |
if ((!wildcard && curr_attr === attribute) || (wildcard && curr_attr.substr(0, attribute.length) === attribute)) { | |
elements.push(arr_elms[i]); | |
} | |
} | |
} | |
return elements; | |
}; | |
/** | |
* Add the element's event to the container. | |
* | |
* @method pushEvent | |
* @param {String} type The event type | |
* @param {String} signature The signature of the event (sha1 of the function) | |
* @param {DOMElement} element The DOM element | |
* @return Add the | |
*/ | |
this.pushEvent = function (type, signature, element) { | |
currentEventContainer.container[type] = currentEventContainer.container[type] || {}; | |
currentEventContainer.container[type][signature] = currentEventContainer.container[type][signature] || []; | |
var identifier = currentEventContainer.getXPath(element); | |
if (identifier === '') { | |
identifier = element.identifier; | |
} | |
if (currentEventContainer.container[type][signature].indexOf(identifier) === -1) { | |
currentEventContainer.container[type][signature].push(identifier); | |
} | |
}; | |
/** | |
* Override the native function "addEventListener". | |
* | |
* @method customAddEventListener | |
* @return undefined | |
*/ | |
this.customAddEventListener = function (type, listener, useCapture, wantsUntrusted) { | |
var signature = currentEventContainer.hashString(listener.toString()); | |
currentEventContainer.pushEvent(type, signature, this); | |
this._origAddEventListener(type, listener, useCapture, wantsUntrusted); | |
}; | |
/** | |
* Override the native function "removeEventListener". | |
* | |
* @method customRemoveEventListener | |
* @param {String} type A string representing the event type being removed. | |
* @param {Function} listener The listener parameter indicates the EventListener function to be removed. | |
* @param {Boolean} useCapture Specifies whether the EventListener being removed was registered as a capturing listener or not. If not specified, useCapture defaults to false. | |
* @return undefined | |
*/ | |
this.customRemoveEventListener = function (type, listener, useCapture) | |
{ | |
var signature = currentEventContainer.hashString(listener.toString()); | |
if (currentEventContainer.container[type][signature] !== undefined) { | |
currentEventContainer.container[type][signature] = undefined; | |
} | |
this._origRemoveEventListener(type, listener, useCapture); | |
}; | |
/** | |
* Override the native function "setAttribute". | |
* | |
* @method customSetAttribute | |
* @param {String} name The name of the attribute as a string. | |
* @param {String} value The desired new value of the attribute. | |
* @return undefined | |
*/ | |
this.customSetAttribute = function (name, value) { | |
var type, signature; | |
if (name.indexOf('on') === 0) { | |
type = name.substr(2); | |
signature = currentEventContainer.hashString(value.toString()); | |
currentEventContainer.pushEvent(type, signature, this); | |
} | |
this._origSetAttribute(name, value); | |
}; | |
/** | |
* Override the native function "removeAttribute". | |
* | |
* @method customRemoveAttribute | |
* @param {String} attrName The attribute to be removed from element. | |
* @return undefined | |
*/ | |
this.customRemoveAttribute = function (attrName) { | |
var type, signature; | |
if (attrName.indexOf('on') === 0) { | |
type = attrName.substr(2); | |
currentEventContainer.container[type] = currentEventContainer.container[type] || {}; | |
signature = currentEventContainer.hashString(this.getAttribute(attrName).toString()); | |
if (currentEventContainer.container[type][signature] !== undefined) { | |
currentEventContainer.container[type][signature] = undefined; | |
} | |
} | |
this._origRemoveAttribute(name); | |
}; | |
/** | |
* Override the event listener for a certain object (e.g.: document, window, | |
* element). | |
* | |
* @method overrideEventListener | |
* @param {Object} object The object that will be changed | |
* @return undefined | |
*/ | |
this.overrideEventListener = function (object) { | |
var prototype = object.prototype === undefined ? object : object.prototype; | |
prototype._origAddEventListener = prototype.addEventListener; | |
prototype.addEventListener = currentEventContainer.customAddEventListener; | |
prototype._origRemoveEventListener = prototype.removeEventListener; | |
prototype.removeEventListener = currentEventContainer.customRemoveEventListener; | |
}; | |
/** | |
* Override the attribute handler for a certain object (e.g.: document, | |
* window, element). | |
* | |
* @method overrideAttributeHandler | |
* @param {Object} object The object that will be changed | |
* @return undefined | |
*/ | |
this.overrideAttributeHandler = function (object) { | |
var prototype = object.prototype === undefined ? object : object.prototype; | |
prototype._origSetAttribute = prototype.setAttribute; | |
prototype.setAttribute = currentEventContainer.customSetAttribute; | |
prototype._origRemoveAttribute = prototype.removeAttribute; | |
prototype.removeAttribute = currentEventContainer.customRemoveAttribute; | |
}; | |
/** | |
* Returns a list of DOM elements grouped by event type. | |
* | |
* @method getEventsGrouped | |
* @param {Array} elements A list of DOM elements to be aggregated | |
* @return {Array} | |
*/ | |
this.getEventsGrouped = function (elements) { | |
var el, i, l, curr_attr, attrs, events = {}; | |
for (el in elements) { | |
var element = elements[el]; | |
for (i = 0, attrs = element.attributes, l = attrs.length; i < l; i++){ | |
curr_attr = attrs.item(i).nodeName; | |
if (curr_attr.substr(0, 2) === 'on') { | |
if (events[curr_attr] === undefined) { | |
events[curr_attr] = []; | |
} | |
events[curr_attr].push(element); | |
} | |
} | |
} | |
return events; | |
}; | |
/** | |
* Returns a list of events bound to any element in the page. | |
* | |
* @method getEvents | |
* @return undefined | |
*/ | |
this.getEvents = function () { | |
var evt, el, type, signature, staticEvents; | |
staticEvents = currentEventContainer.getEventsGrouped(currentEventContainer.getElementsByAttribute(document, 'on*')); | |
for (evt in staticEvents) { | |
type = evt.substr(2); | |
for (el in staticEvents[evt]) { | |
signature = currentEventContainer.hashString(staticEvents[evt][el].getAttribute(evt)); | |
currentEventContainer.pushEvent(type, signature, staticEvents[evt][el]); | |
} | |
} | |
return currentEventContainer.container; | |
}; | |
/** | |
* Retrieve a DOM element by XPath. | |
* | |
* @method getElementByXpath | |
* @param {String} path The XPath expression | |
* @return {DOMElement} | |
*/ | |
this.getElementByXpath = function (path) { | |
return document.evaluate(path, document, null, 9, null).singleNodeValue; | |
}; | |
}; | |
var eventContainer = new EventContainer(); | |
if (typeof Element !== 'undefined') { | |
eventContainer.overrideEventListener(Element); | |
eventContainer.overrideAttributeHandler(Element); | |
} | |
if (typeof document !== 'undefined') { | |
document.identifier = 'document'; | |
eventContainer.overrideEventListener(document); | |
eventContainer.overrideAttributeHandler(document); | |
} | |
if (typeof window !== 'undefined') { | |
window.identifier = 'window'; | |
eventContainer.overrideEventListener(window); | |
eventContainer.overrideAttributeHandler(window); | |
window.eventContainer = eventContainer; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It there a native implementation for this?