81

I'm trying to figure out how to listen for all events on a JavaScript object.

I know that I can add individual events with something like this

element.addEventListener("click", myFunction);
element.addEventListener("mouseover", myFunction);
...

I'm trying to figure out if there is a catch-all, I'd like to do something like this:

// Begin pseudocode
var myObj = document.getElementById('someID');

myObj.addEventListener(/*catch all*/, myFunction);

function myFunction() {
  alert(/*event name*/);
}
// End pseudocode
user3780616
  • 1,183
  • 1
  • 10
  • 23
  • 4
    Why would you want to do that? – putvande Dec 05 '14 at 17:42
  • 3
    Did you ever figure this out? If it is doable or not? – hipkiss Aug 04 '16 at 13:30
  • 9
    @putvande Why not? And no, it's not really possible to start listen to ALL events with one function call. You need to know the names. See answers below. I think user3780616 is trying to find an event that fires when he does a distinct action. He is trying to FIND the name of an event. – Bitterblue Oct 18 '18 at 14:57
  • @putvande, maybe you are making a custom input web component, and wish to expose all the normal events, without create slowing the browser down a lot by create an event listener for each event type, for instance, which is a massively exploding problem. – run_the_race Mar 20 '23 at 07:16

7 Answers7

94

A more modern rewrite of @roman-bekkiev's answer:

Object.keys(window).forEach(key => {
    if (/^on/.test(key)) {
        window.addEventListener(key.slice(2), event => {
            console.log(event);
        });
    }
});

Here is an example to further customize event filtering:

/^on(key|mouse)/.test(key)

ryanpcmcquen
  • 6,285
  • 3
  • 24
  • 37
  • 1
    Pay attention that this will not work on IE because of the arrow function ('=>'). Use Roman's answer if you want to use eventListener on IE. – user2018 Oct 25 '19 at 10:09
  • @ryanpcmcquen I mean, you should use several `Object.getPrototypeOf()` on child html node in order to get event properties. – Jehong Ahn Jan 16 '20 at 08:00
  • Perfect! Exactly what i am looking for. If you like to remove spamming events use /^on(?!mouse|pointer)/ – Taurus Nov 11 '22 at 10:26
  • Object.keys does not list most event listeners – golopot Dec 02 '22 at 06:28
  • Here is a more comprehensive list of events that might spam! `animationiteration, animationstart, click, dblclick, mouseout, mouseevent, mousemove, mouseover, mousein, pointerout, pointerup, pointermove, pointerrawupdate, pointerover, pointerdown, blur, mousedown, mouseup, transitionrun, transitionstart, transitioncancel, transitionend, scroll, scrollend` – BoKKeR Jul 13 '23 at 09:17
51

To pick up standard element's events.

var myObj = document.getElementById('someID')
for (var key in myObj) {
    if (key.search('on') === 0) {
       myObj.addEventListener(key.slice(2), myFunction)
    }
}

But as @jeremywoertink mentioned any other events are also possible...

pizzaisdavid
  • 455
  • 3
  • 13
Roman Bekkiev
  • 3,010
  • 1
  • 24
  • 31
  • what about all events on window? –  Jan 02 '18 at 01:21
  • 4
    @Olegzandr, what's the problem with window? Just replace `myObj` with `window`. However, note that with this you _also_ would listen to some non-existing events, e.g. if there was a global variable `onetwothree` the code code above would add a listener to event `'etwothree'`. ( ͡° ͜ʖ ͡°) – Roman Bekkiev Jan 11 '18 at 10:32
10

I hate that this problem persists without a native or elegant solution.

A Better Solution?

This allows you to subscribe to a single CustomEvent for any EventTarget using target.addEventListener('*', ...).

    clear();

    /**
     * @param : source := EventTarget
     *  *   EventTarget.prototype
     *  *   Node (Element, Attr, etc)
     * @usage : [Node].addEventListener('*', ({ detail: e }) => {...}, false);
     */
    function proxyEventTargetSource(source) {
        var emit = source.dispatchEvent;  // obtain reference

        function proxy(event) {
            var { type } = event, any = new CustomEvent('*', { detail: event });  // use original event as detail
            if (!{ '*': true }[ type ]) emit.call(this, any);  // only emit "any" if type is not any.type ('*')
            return emit.call(this, event);
        }

        if ({ 'dispatchEvent': true }[ emit.name ]) source.dispatchEvent = proxy;  // attempt overwrite only if not already set (avoid rewrapping)
        return (source.dispatchEvent === proxy);  // indicate if its set after we try to
    }

    // proxyEventTargetSource(EventTarget.prototype);  // all targets
    proxyEventTargetSource(document);  // single target
    var e = new CustomEvent('any!', { detail: true });
    document.addEventListener('*', (e) => console.log('type: %s, original: %s, e: %O', e.type, e.detail.type, e), false);
    document.dispatchEvent(e);

Granted, a more native or [perhaps] more elegant way would be to use a native Proxy on apply for the target's dispatchEvent method, but that would maybe convey less for the sake of this post.

Gist: https://gist.github.com/cScarlson/875a9fca7ab7084bb608fb66adff0463

Known Issues

Apparently, this only works while driving event-dispatching through EventTargets's dispatchEvent method. That is, naturally triggering events through mouse events (for instance) does not work. There would need to be a way to wrap the internal method being called by natural event-triggers.

That being said, if you have a way around this, please show what you have in another answer.

halfer
  • 19,824
  • 17
  • 99
  • 186
Cody
  • 9,785
  • 4
  • 61
  • 46
3

As far as I know, it's possible.


For all native events, we can retrieve a list of supported events by iterating over the target.onevent properties and installing our listener for all of them.

for (const key in target) {
    if(/^on/.test(key)) {
        const eventType = key.substr(2);
        target.addEventListener(eventType, listener);
    }
}

The only other way that events are emitted which I know of is via EventTarget.dispatchEvent, which every Node and thefore every Element inherits.
To listen for all these manually triggered events, we can proxy the dispatchEvent method globally and install our listener just-in-time for the event whose name we just saw ✨ ^^

const dispatchEvent_original = EventTarget.prototype.dispatchEvent;
EventTarget.prototype.dispatchEvent = function (event) {
    if (!alreadyListenedEventTypes.has(event.type)) {
        target.addEventListener(event.type, listener, ...otherArguments);
        alreadyListenedEventTypes.add(event.type);
    }
    dispatchEvent_original.apply(this, arguments);
};

function snippet

function addEventListenerAll(target, listener, ...otherArguments) {

    // install listeners for all natively triggered events
    for (const key in target) {
        if (/^on/.test(key)) {
            const eventType = key.substr(2);
            target.addEventListener(eventType, listener, ...otherArguments);
        }
    }

    // dynamically install listeners for all manually triggered events, just-in-time before they're dispatched ;D
    const dispatchEvent_original = EventTarget.prototype.dispatchEvent;
    function dispatchEvent(event) {
        target.addEventListener(event.type, listener, ...otherArguments);  // multiple identical listeners are automatically discarded
        dispatchEvent_original.apply(this, arguments);
    }
    EventTarget.prototype.dispatchEvent = dispatchEvent;
    if (EventTarget.prototype.dispatchEvent !== dispatchEvent) throw new Error(`Browser is smarter than you think!`);

}


// usage example
addEventListenerAll(window, (evt) => {
    console.log(evt.type);
});
document.body.click();
document.body.dispatchEvent(new Event('omg!', { bubbles: true }));


// usage example with `useCapture`
// (also receives `bubbles: false` events, but in reverse order)
addEventListenerAll(
    window,
    (evt) => { console.log(evt.type); },
    true
);
document.body.dispatchEvent(new Event('omfggg!', { bubbles: false }));
pitizzzle
  • 303
  • 3
  • 8
2

You could use EventEmitter2 which does wildcards. The problem with doing a catchall like you're talking about is that there are so many events, and you can create your own. You'd have to make an array of specifically which events you're talking about, iterate over that, and bind each one individually.

jeremywoertink
  • 2,281
  • 1
  • 23
  • 29
1

You should probably pick the events you want to listen to, put them into an array and iterate over each:

['click','mouseover'].forEach(function(ev) {
    el.addEventListener(ev, function() {
        console.log('event:', ev)
    })
})
Mosho
  • 7,099
  • 3
  • 34
  • 51
-1
//listening for all click events on the document
   document.addEventListener('click', function (event) {

    //filtering for only events that happen on elements that contain the class
    //view_btn.          
  if (event.target.classList.contains( 'view_btn' )){
//logging out the id of the element        
          var id_of_clicked_element = event.target.getAttribute("id"); //
          console.log("button clicked has is of " + id_of_clicked_element)

        }
    });