Skip to content

Instantly share code, notes, and snippets.

@fabiocicerchia
Last active March 19, 2018 16:27
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fabiocicerchia/7116129 to your computer and use it in GitHub Desktop.
Save fabiocicerchia/7116129 to your computer and use it in GitHub Desktop.
JavaScript - Get events bound to DOM elements
/**
* 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;
}
@oychao
Copy link

oychao commented Jul 27, 2017

It there a native implementation for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment