Get event listeners attached to node using addEventListener
Asked Answered
E

5

132

I have already looked at these questions:

however none of them answers how to get a list of event listeners attached to a node using addEventListener, without modifying the addEventListener prototype before the event listeners are created.

VisualEvent doesn't display all event listener (iphone specific ones) and I want to do this (somewhat) programmatically.

Ehf answered 28/1, 2012 at 16:16 Comment(2)
Possible duplicate of How to find event listeners on a DOM node when debugging or from the JS code?Kano
"somewhat programmatically" and the fact that accepted answer for this question is a devtools feature makes this a duplicate of the listed question. For those looking for a JS solution, the answer is "there isn't one"Kano
G
160

Chrome DevTools, Safari Inspector and Firebug support getEventListeners(node).

getEventListeners(document)

Goldfinch answered 27/3, 2013 at 18:13 Comment(3)
I want to note that getEventListeners method is not support Firefox 35 version.Rooted
It might not work on Firefox, but then again devs develop on multiple browsers/ This sure HELPS if one needs to modify an existing site... A LOT!Quadragesimal
Not it Firefox 69.Anissaanita
B
78

You can't.

The only way to get a list of all event listeners attached to a node is to intercept the listener attachment call.

DOM4 addEventListener

Says

Append an event listener to the associated list of event listeners with type set to type, listener set to listener, and capture set to capture, unless there already is an event listener in that list with the same type, listener, and capture.

Meaning that an event listener is added to the "list of event listeners". That's all. There is no notion of what this list should be nor how you should access it.

Blackett answered 5/4, 2012 at 14:42 Comment(5)
Any chance of supplying some justification or reasoning for why it must work this way? Clearly the browser knows what all of the listeners are.Tripody
@user973810: How do you want him to justify this? The DOM API provides no way to do it and there are no non-standard ways to do it in current browsers. As to why this is the case, I don't really know. It seems a reasonable thing to want to do.Cymbal
I've seen a few threads lying around about adding an API to the DOM for this.Blackett
@TimDown the edit helps. Seeing that there is no specification for something like "getEventListeners" justifies why there isn't such a thing.Tripody
I find it useful for things like Electron apps or Chrome extensions. Some event listeners may be injected to the page, but the remote page has no clue - and that's good.Puritanism
E
27

Since there is no native way to do this ,Here is the less intrusive solution i found (dont add any 'old' prototype methods):

var ListenerTracker=new function(){
    var targets=[];
    // listener tracking datas
    var _elements_  =[];
    var _listeners_ =[];
    this.init=function(){
        this.listen(Element,window);
    };
    this.listen=function(){
        for(var i=0;i<arguments.length;i++){
            if(targets.indexOf(arguments[i])===-1){
                targets.push(arguments[i]);//avoid duplicate call
                intercep_events_listeners(arguments[i]);
            }
        }
    };
    // register individual element an returns its corresponding listeners
    var register_element=function(element){
        if(_elements_.indexOf(element)==-1){
            // NB : split by useCapture to make listener easier to find when removing
            var elt_listeners=[{/*useCapture=false*/},{/*useCapture=true*/}];
            _elements_.push(element);
            _listeners_.push(elt_listeners);
        }
        return _listeners_[_elements_.indexOf(element)];
    };
    var intercep_events_listeners = function(target){
        var _target=target;
        if(target.prototype)_target=target.prototype;
        if(_target.getEventListeners)return;
        if(typeof(_target.addEventListener)!=='function'||typeof(_target.removeEventListener)!=='function'){
            console.log('target=',target);
            throw('\nListenerTracker Error:\nUnwrappable target.');
        }
        // backup overrided methods
        var _super_={
            "addEventListener"      : _target.addEventListener,
            "removeEventListener"   : _target.removeEventListener
        };

        _target["addEventListener"]=function(type, listener, useCapture){
            var listeners=register_element(this);
            // add event before to avoid registering if an error is thrown
            _super_["addEventListener"].apply(this,arguments);
            // adapt to 'elt_listeners' index
            var uc=(typeof(useCapture)==='object'?useCapture.useCapture:useCapture)?1:0;
            if(!listeners[uc][type])listeners[uc][type]=[];
            listeners[uc][type].push({cb:listener,args:arguments});
        };
        _target["removeEventListener"]=function(type, listener, useCapture){
            var listeners=register_element(this);
            // add event before to avoid registering if an error is thrown
            _super_["removeEventListener"].apply(this,arguments);
            // adapt to 'elt_listeners' index
            useCapture=(typeof(useCapture)==='object'?useCapture.useCapture:useCapture)?1:0;
            if(!listeners[useCapture][type])return;
            var lid = listeners[useCapture][type].findIndex(obj=>obj.cb===listener);
            if(lid>-1)listeners[useCapture][type].splice(lid,1);
        };
        _target["getEventListeners"]=function(type){
            var listeners=register_element(this);
            // convert to listener datas list
            var result=[];
            for(var useCapture=0,list;list=listeners[useCapture];useCapture++){
                if(typeof(type)=="string"){// filtered by type
                    if(list[type]){
                        for(var id in list[type]){
                            result.push({
                                "type":type,
                                "listener":list[type][id].cb,
                                "args":list[type][id].args,
                                "useCapture":!!useCapture
                            });
                        }
                    }
                }else{// all
                    for(var _type in list){
                        for(var id in list[_type]){
                            result.push({
                                "type":_type,
                                "listener":list[_type][id].cb,
                                "args":list[_type][id].args,
                                "useCapture":!!useCapture
                            });
                        }
                    }
                }
            }
            return result;
        };
    };

}();


ListenerTracker.init();

EDIT

Suggestion from @mplungjan: modified to listen to wrappable targets (singleton|constructor). 'init' tracks Element and window .

exemple with other wrappable target:

ListenerTracker.listen(XMLHttpRequest);

Suggestion from @kodfire : You may get optionals arguments with the args property.

Europeanize answered 6/2, 2016 at 0:29 Comment(9)
You should also make it intercept window event listeners. Other than that, this works great!Pimentel
Can we use a similar script to intercept window.addEventListener?Metaphysic
The window object is a singleton and doesn't inherit from Element. If you want to intercept window.addEventListener, you will have to copy this code and replace Element.prototype and HTMLElement.prototype by window.Europeanize
Awesome :)) Is it possible to call the getEventListeners in javascript and not in devtools?Linage
@Linage Thank you. The tracker overloads HTMLElement event methods globally. So once you called ListenerTracker.init(); every new event will be stacked and you can call element.getEventListeners(); from any script.Europeanize
@Europeanize Thank you back :). It just has the useCapture. How to add the value of passive too?Linage
@Metaphysic Thank you for your suggestion. I edited the code accordingly. On the post where i suggested to copy/paste, i didnt have this pattern in mind.Europeanize
@Linage Thank you for your suggestion. Since the edit, If for exemple you use options as 3rd argument, you can get passive with obj.args[2].passive .Europeanize
Thank you very much @Europeanize for your update. Now I want to get the caller location in order to add {passive: true} to them. How can I find them as they might be called from different places. I used var caller_line = (new Error).stack.split("\n")[4]; inside _target["addEventListener"] function but the value is undefined.Linage
F
3

I can't find a way to do this with code, but in stock Firefox 64, events are listed next to each HTML entity in the Developer Tools Inspector as noted on MDN's Examine Event Listeners page and as demonstrated in this image:

screen shot of FF Inspector

Firooc answered 21/1, 2019 at 23:54 Comment(0)
C
1

You can obtain all jQuery events using $._data($('[selector]')[0],'events'); change [selector] to what you need.

There is a plugin that gather all events attached by jQuery called eventsReport.

Also i write my own plugin that do this with better formatting.

But anyway it seems we can't gather events added by addEventListener method. May be we can wrap addEventListener call to store events added after our wrap call.

It seems the best way to see events added to an element with dev tools.

But you will not see delegated events there. So there we need jQuery eventsReport.

UPDATE: NOW We CAN see events added by addEventListener method SEE RIGHT ANSWER TO THIS QUESTION.

Cantina answered 27/7, 2013 at 20:47 Comment(3)
This is a private and deprecated interface & it may go away soon so don't rely on that.Haematoblast
Yep, but the time i answered, there was no such ability in Dev tools. So, there was nothing to choose from.Cantina
It is deprecated @Rantiev, can you remove that answer?Brusquerie

© 2022 - 2024 — McMap. All rights reserved.