How to find event listeners on a DOM node in JavaScript or in debugging?

Viewed 769370

I have a page where some event listeners are attached to input boxes and select boxes. Is there a way to find out which event listeners are observing a particular DOM node and for what event?

Events are attached using:

  1. Prototype's Event.observe;
  2. DOM's addEventListener;
  3. As element attribute element.onclick.
20 Answers

It depends on how the events are attached. For illustration presume we have the following click handler:

var handler = function() { alert('clicked!') };

We're going to attach it to our element using different methods, some which allow inspection and some that don't.

Method A) single event handler

element.onclick = handler;
// inspect
console.log(element.onclick); // "function() { alert('clicked!') }"

Method B) multiple event handlers

if(element.addEventListener) { // DOM standard
    element.addEventListener('click', handler, false)
} else if(element.attachEvent) { // IE
    element.attachEvent('onclick', handler)
}
// cannot inspect element to find handlers

Method C): jQuery

$(element).click(handler);
  • 1.3.x

     // inspect
     var clickEvents = $(element).data("events").click;
     jQuery.each(clickEvents, function(key, value) {
         console.log(value) // "function() { alert('clicked!') }"
     })
    
  • 1.4.x (stores the handler inside an object)

     // inspect
     var clickEvents = $(element).data("events").click;
     jQuery.each(clickEvents, function(key, handlerObj) {
         console.log(handlerObj.handler) // "function() { alert('clicked!') }"
         // also available: handlerObj.type, handlerObj.namespace
     })
    
  • 1.7+ (very nice)

    Made using knowledge from this comment.

     events = $._data(this, 'events');
     for (type in events) {
       events[type].forEach(function (event) {
         console.log(event['handler']);
       });
     }
    

(See jQuery.fn.data and jQuery.data)

Method D): Prototype (messy)

$(element).observe('click', handler);
  • 1.5.x

     // inspect
     Event.observers.each(function(item) {
         if(item[0] == element) {
             console.log(item[2]) // "function() { alert('clicked!') }"
         }
     })
    
  • 1.6 to 1.6.0.3, inclusive (got very difficult here)

     // inspect. "_eventId" is for < 1.6.0.3 while 
     // "_prototypeEventID" was introduced in 1.6.0.3
     var clickEvents = Event.cache[element._eventId || (element._prototypeEventID || [])[0]].click;
     clickEvents.each(function(wrapper){
         console.log(wrapper.handler) // "function() { alert('clicked!') }"
     })
    
  • 1.6.1 (little better)

     // inspect
     var clickEvents = element.getStorage().get('prototype_event_registry').get('click');
     clickEvents.each(function(wrapper){
         console.log(wrapper.handler) // "function() { alert('clicked!') }"
     })
    

When clicking the resulting output in the console (which shows the text of the function), the console will navigate directly to the line of the function's declaration in the relevant JS file.

WebKit Inspector in Chrome or Safari browsers now does this. It will display the event listeners for a DOM element when you select it in the Elements pane.

Paste in console to get all eventListeners printed beside their HTML element

Array.from(document.querySelectorAll("*")).forEach(element => {
    const events = getEventListeners(element)
    if (Object.keys(events).length !== 0) {
         console.log(element, events)
    }
})

If you have Firebug, you can use console.dir(object or array) to print a nice tree in the console log of any JavaScript scalar, array, or object.

Try:

console.dir(clickEvents);

or

console.dir(window);

Opera 12 (not the latest Chrome Webkit engine based) Dragonfly has had this for a while and is obviously displayed in the DOM structure. In my opinion it is a superior debugger and is the only reason remaining why I still use the Opera 12 based version (there is no v13, v14 version and the v15 Webkit based lacks Dragonfly still)

enter image description here

I was recently working with events and wanted to view/control all events in a page. Having looked at possible solutions, I've decided to go my own way and create a custom system to monitor events. So, I did three things.

First, I needed a container for all the event listeners in the page: that's theEventListeners object. It has three useful methods: add(), remove(), and get().

Next, I created an EventListener object to hold the necessary information for the event, i.e.: target, type, callback, options, useCapture, wantsUntrusted, and added a method remove() to remove the listener.

Lastly, I extended the native addEventListener() and removeEventListener() methods to make them work with the objects I've created (EventListener and EventListeners).

Usage:

var bodyClickEvent = document.body.addEventListener("click", function () {
    console.log("body click");
});

// bodyClickEvent.remove();

addEventListener() creates an EventListener object, adds it to EventListeners and returns the EventListener object, so it can be removed later.

EventListeners.get() can be used to view the listeners in the page. It accepts an EventTarget or a string (event type).

// EventListeners.get(document.body);
// EventListeners.get("click");

Demo

Let's say we want to know every event listener in this current page. We can do that (assuming you're using a script manager extension, Tampermonkey in this case). Following script does this:

// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @include      https://stackoverflow.com/*
// @grant        none
// ==/UserScript==

(function() {
    fetch("https://raw.githubusercontent.com/akinuri/js-lib/master/EventListener.js")
        .then(function (response) {
            return response.text();
        })
        .then(function (text) {
            eval(text);
            window.EventListeners = EventListeners;
        });
})(window);

And when we list all the listeners, it says there are 299 event listeners. There "seems" to be some duplicates, but I don't know if they're really duplicates. Not every event type is duplicated, so all those "duplicates" might be an individual listener.

screenshot of console listing all event listeners in this page

Code can be found at my repository. I didn't want to post it here because it's rather long.


Update: This doesn't seem to work with jQuery. When I examine the EventListener, I see that the callback is

function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}

I believe this belongs to jQuery, and is not the actual callback. jQuery stores the actual callback in the properties of the EventTarget:

$(document.body).click(function () {
    console.log("jquery click");
});

enter image description here

To remove an event listener, the actual callback needs to be passed to the removeEventListener() method. So in order to make this work with jQuery, it needs further modification. I might fix that in the future.

Update 2022:

In the Chrome Developer Tools, in the Elements panel, there is the Event Listeners tab, where you can see listeners for the element.

You can also unselect "Ancestors" so it only shows the listeners for that element

Developer Tools

changing these functions will allow you to log the listeners added:

EventTarget.prototype.addEventListener
EventTarget.prototype.attachEvent
EventTarget.prototype.removeEventListener
EventTarget.prototype.detachEvent

read the rest of the listeners with

console.log(someElement.onclick);
console.log(someElement.getAttribute("onclick"));
Related