23

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.

3
  • Where do you see Chrome or Firebug display event listeners? Commented Jun 7, 2012 at 21:32
  • Yeah, I would love to know where you see them too! I've downloaded plugins for Firebug that show them, but you're probably right that they somehow track any event listeners when they are bound Commented Jun 7, 2012 at 21:37
  • 1
    In chrome web inspector click on the 'elements' tab at the top. it's the one furthest tp the left. Select an element in the dom and then on the right hand side there is a list of information about the element you have selected like Styles, Properties etc. The last in the list is Event Listeners and it give you all the information you need. But You can only see the info for each element selected, not the entire DOM. Hense me asking the question:) Commented Jun 8, 2012 at 6:23

5 Answers 5

35

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.

Sign up to request clarification or add additional context in comments.

3 Comments

getEventListeners(myDomElement) works in the Chromium v.25 developer tools console, but in actual code it throws an error.
@Robinlikethebird yes, getEventListeners is a method that chrome devtools console provide, I am looking for a method that can find events bound to dom at runtime. for now, I just find jquery method $.data(element, 'events'). I am looking for a plain javascript or DOM way.
Also works in Edge Chromium nowadays
6

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?

Comments

2

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...

2 Comments

"the correct approach is the Robin like the bird's way" what's this supposed to mean?
@Neutrino ops... sorry I had something into the clipboard I pasted here for mistake without even realizing it. I've corrected the phrase. Thanks for the notice.
0

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.

Comments

0

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());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.