Reputation: 18795
I still can't belive this is not possible but is there a way to loop through the dom and see all event handlers attached using 'addEventListener'. This post and many others says no.
If that's the case then how do apps like Chrome's inspector or Firebug show them, which they do? I guess they probably augment the dom's methods in some way so they can track what's being bound.
Upvotes: 23
Views: 52492
Reputation: 82156
Yes it is possible in plain JS even, but only if you intercept/override the addEventListener and removeEventListener prototype functions with your own interceptor, so you can intercept them.
This will work for anything that has been added with addEventListener (and it accounts for removeEventListener).
But if you added them without EventListener, e.g. with element.onclick (or in the onclick/onAnything-attribute in the markup), this won't list them, you'll have to manually check for them.
Be sure that the bellow JavaScript is the first script that is executed on your page, otherwise it might not work properly.
Here's how (TypeScript):
type EventHandlerMapType = {
// [key: EventTarget]: { [type: string]: EventListenerOrEventListenerObject[] };
[key: string]: { [type: string]: EventListenerOrEventListenerObject[] };
};
type EventHandlerMapValue = { [type: string]: EventListenerOrEventListenerObject[] };
interface EventTarget
{
getEventHandlers: (type?: string) => EventHandlerMapValue | EventListenerOrEventListenerObject[];
}
// function addEventListener<K extends keyof ElementEventMap>(type: K, listener: (this: Element, ev: ElementEventMap[K]) => any, options ?: boolean | AddEventListenerOptions): void;
// addEventListener(type: string, listener: EventListenerOrEventListenerObject, options ?: boolean | AddEventListenerOptions): void;
(function ()
{
// Store the handlers by element reference
// WeakMap can take an object, such as an Element, as a key, object cannot.
// This is useful because WeakMap allows for garbage collection of the keys(the elements),
// meaning when an Element is removed from the DOM and no longer referenced, it gets garbage - collected,
// and its entry in the WeakMap is automatically removed.
// This prevents memory leaks.
const eventHandlerMap = new WeakMap<EventTarget>(); // Dictionary<Element, { type:[]}> // where type is string and array is an array of handlers/listeners
// Override the native addEventListener
const originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions)
{
// Call the original addEventListener to ensure normal behavior
originalAddEventListener.call(this, type, listener, options);
// Initialize tracking for the current element if it doesn't exist
if (!eventHandlerMap.has(this))
{
eventHandlerMap.set(this, {});
}
// Get the event type handlers for this element
const handlersForElement = eventHandlerMap.get(this);
if (!handlersForElement[type])
{
handlersForElement[type] = [];
}
// Add the handler to the list for this event type
handlersForElement[type].push(listener);
};
// Override the native removeEventListener
const originalRemoveEventListener = EventTarget.prototype.removeEventListener;
EventTarget.prototype.removeEventListener = function (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions)
{
// Call the original removeEventListener to ensure normal behavior
originalRemoveEventListener.call(this, type, listener, options);
// Remove the handler from the tracking list
if (eventHandlerMap.has(this))
{
const handlersForElement = eventHandlerMap.get(this);
if (handlersForElement[type])
{
// Filter out the handler that matches the one being removed
handlersForElement[type] = handlersForElement[type].filter((h: EventListenerOrEventListenerObject) => h !== listener);
// Clean up if no handlers left for this event type
if (handlersForElement[type].length === 0)
{
delete handlersForElement[type];
}
}
// Clean up the element if no handlers left for any event type
if (Object.keys(handlersForElement).length === 0)
{
eventHandlerMap.delete(this);
}
}
};
// Function to retrieve all event handlers for an element
EventTarget.prototype.getEventHandlers = function (type?: string): EventHandlerMapValue | EventListenerOrEventListenerObject[]
{
// Get the tracking list for the current element
const handlersForElement = eventHandlerMap.get(this) || {};
if (type)
{
// If a specific event type is requested, return its handlers
return handlersForElement[type] || [];
}
// If no type is specified, return all handlers grouped by type
return handlersForElement;
};
})();
Now on EventTarget (Element, Node, etc):
getEventHandlers(type?: string)
or in plain-JS
(function () {
var eventHandlerMap = new WeakMap();
var originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (type, listener, options) {
originalAddEventListener.call(this, type, listener, options);
if (!eventHandlerMap.has(this)) {
eventHandlerMap.set(this, {});
}
var handlersForElement = eventHandlerMap.get(this);
if (!handlersForElement[type]) {
handlersForElement[type] = [];
}
handlersForElement[type].push(listener);
};
var originalRemoveEventListener = EventTarget.prototype.removeEventListener;
EventTarget.prototype.removeEventListener = function (type, listener, options) {
originalRemoveEventListener.call(this, type, listener, options);
if (eventHandlerMap.has(this)) {
var handlersForElement = eventHandlerMap.get(this);
if (handlersForElement[type]) {
handlersForElement[type] = handlersForElement[type].filter(function (h) { return h !== listener; });
if (handlersForElement[type].length === 0) {
delete handlersForElement[type];
}
}
if (Object.keys(handlersForElement).length === 0) {
eventHandlerMap.delete(this);
}
}
};
EventTarget.prototype.getEventHandlers = function (type) {
var handlersForElement = eventHandlerMap.get(this) || {};
if (type) {
return handlersForElement[type] || [];
}
return handlersForElement;
};
})();
Tests:
var btnCreated = document.createElement("button");
btnCreated.textContent = "Hello Kitty";
btnCreated.value = "Hello Kitty";
document.body.appendChild(btnCreated);
var btn = document.querySelector('button');
function handleClick() {
console.log('Button clicked');
}
btn.addEventListener('click', handleClick);
btn.addEventListener('clock', handleClick);
console.log(btn.getEventHandlers('click'));
console.log("before click");
btn.click();
console.log("after click");
btn.removeEventListener('click', handleClick);
console.log("before click after click removed");
btn.click();
console.log("after click after click removed");
console.log("click handlers", btn.getEventHandlers('click'));
console.log("all handlers", btn.getEventHandlers());
Upvotes: 0
Reputation: 3445
The console of Chrome has a method that can help you check if a dom node has any event listeners registered; for example to check event listeners attached to the document node using:
https://developers.google.com/chrome-developer-tools/docs/commandline-api#geteventlistenersobject
getEventListeners(document);
If needed, you could recursively iterate over all dom nodes and find all event handlers attached.
Upvotes: 35
Reputation: 1686
On Chrome v53 console I tried:
getEventListeners(document);
that returns:
__proto__: Object
and sub elements, not what I'm looking for.
So I've tried:
getEventListeners(window);
that returns
Object {beforeunload: Array[1], load: Array[1]}
That is what I'm looking for. So I think that the correct approach is:
getEventListeners(myDomElement):
where myDomElement is the targeted object got with standard ways like getElementById etc...
Upvotes: 2
Reputation: 2259
use the following function to fetch the json of registered events;
getEventListeners(node_name);
where node_name
can be an element name or its id.
Upvotes: 0
Reputation: 152956
Of course browsers internally have a list of event listeners, but it is not exposed to page-level JavaScript. For example, Firebug (or Eventbug) probably use nsIEventListenerInfo.
That being said, this old answer still holds:
How to find event listeners on a DOM node?
Upvotes: 6