How can I add an event listener for all events in javascript without listing them individually?

28,918

Solution 1

There's no wild card, but using jQuery you can have 1 long event listener line, rather than multiple.

How do you log all events fired by an element in jQuery?

Like so:

('body').on("click mousedown mouseup focus blur keydown change dblclick mousemove mouseover mouseout mousewheel keydown keyup keypress textInput touchstart touchmove touchend touchcancel resize scroll zoom select change submit reset",function(e){
     console.log(e);
}); 

Solution 2

A little late to the party, but here's how I add all event listeners & log them to the console:

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

Solution 3

How to implement addEventListener('*') yourself


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

Solution 4

I'm not a 100% if this is what you're looking for, but I think this is what you're looking for.

the document which is what we usually tag into for our events doesn't contain the events. It's actually the grandparent class.

document -> HTMLDocument -> Document (where you could find the onclick* events)

as you could see we can then do this. just trim the on at the start of the events

Object.keys(document.__proto__.__proto__).reduce((arr, event)=> {
  if(event.startsWith('on')) return [...arr, event.substr(2)];
  return arr;
}, [])

which would return something like:

["readystatechange", "pointerlockchange", "pointerlockerror", "beforecopy", "beforecut", "beforepaste", "freeze", "resume", "search", "securitypolicyviolation", "visibilitychange", "copy", "cut", "paste", "abort", "blur", "cancel", "canplay", "canplaythrough", "change", "click", "close", "contextmenu", "cuechange", "dblclick", "drag", "dragend", "dragenter", "dragleave", "dragover", "dragstart", "drop", "durationchange", "emptied", "ended", "error", "focus", "formdata", "input", "invalid", "keydown", "keypress", "keyup", "load", "loadeddata", "loadedmetadata", "loadstart", "mousedown", "mouseenter", "mouseleave", "mousemove", "mouseout", "mouseover", "mouseup", "mousewheel", "pause", "play", "playing", "progress", "ratechange", "reset", "resize", "scroll", "seeked", "seeking", "select", "stalled", "submit", "suspend", "timeupdate", "toggle", "volumechange", "waiting", "webkitanimationend", "webkitanimationiteration", "webkitanimationstart", "webkittransitionend", "wheel", "auxclick", "gotpointercapture", "lostpointercapture", "pointerdown", "pointermove", "pointerup", "pointercancel", "pointerover", "pointerout", "pointerenter", "pointerleave", "selectstart", "selectionchange", "animationend", "animationiteration", "animationstart", "transitionend", "fullscreenchange", "fullscreenerror", "webkitfullscreenchange", "webkitfullscreenerror", "pointerrawupdate"]

this is just something quick I came up with and I'm sure there are better answers from smarter folks. you might also want to check out https://developer.mozilla.org/en-US/docs/Web/Events which contains what you just might need.

Solution 5

getElementsByClassName already returns an array (or more specifically, an HTMLCollection), not a single element. Just do it in a loop.

myObject.element = document.getElementsByClassName('js-myObject');
for(var i=0; i<myObject.element.length; i++){
    myObject.element[i].addEventListener('click', myObject.controller);
    myObject.element[i].addEventListener('mouseover', myObject.controller);
}

Edit... after clarification.. loops and arrays are still useful

var e = ['click', 'hover', 'focus', 'mouseover', 'mouseout'];
myObject.element = document.getElementsByClassName('js-myObject');
for(var i=0; i<myObject.element.length; i++){
    for(var n=0; n<e.length; n++)
        myObject.element[i].addEventListener(e[n], myObject.controller);
}
Share:
28,918
Ken Sherman
Author by

Ken Sherman

Updated on August 07, 2021

Comments

  • Ken Sherman
    Ken Sherman almost 3 years

    I'd like to accomplish the following code using a wildcard (that I don't think exists?)

    myObject.element = document.getElementsByClassName('js-myObject');
    myObject.element.addEventListener('click', myObject.click);
    myObject.element.addEventListener('mouseover', myObject.mouseover);
    //etc..
    

    So far, I have the following

    myObject.controller = function(e){
       if( (e.type in myObject) && (typeof myObject[e.type] ==='function') ){
          myObject[e.type](e);
       }
    };
    
    //but the listeners still have to be assigned as such
    myObject.element = document.getElementsByClassName('js-myObject');
    myObject.element.addEventListener('click', myObject.controller);
    myObject.element.addEventListener('mouseover', myObject.controller);
    //etc...
    
    
    // but I want to do (and it doesn't work)
    myObject.element.addEventListener('*', myObject.controller);
    

    Any suggestions on methods other than an array of events and a foreach statement?

    Edit, my current solution

    I've accepted an answer below (there's no wildcard available, and it's a bad idea to parse potentially hundreds of events)

    For those looking for a similar function, I've settled on the following approach, at least for now.

    for(var prop in myObject){
      if (!myObject.hasOwnProperty(prop){continue;}
      if (typeof myObject[prop] !== 'function'){continue;}
      myObject.element.addEventListener(prop, myObject[prop]);
    }
    

    The upside is a handler for custom events that I don't have to go back and add listeners for. The downside is that I have to ensure this function is called after every myObject.someEvent() is defined. I call it in myObject.init(); which works for me just fine. Note that this solution wouldn't fit my previous specs, because it uses a for/each loop -- but it accomplishes what i really wanted to accomplish and a big thanks to @torazaburo for clearly defining the technical limitations and lack of wisdom in my initial plan.

  • Ken Sherman
    Ken Sherman over 7 years
    I believe you're missing the point of my question. It's not to add the same listeners to multiple objects. it's to add all listeners to a single object without listing every event listener
  • Don Cheadle
    Don Cheadle over 7 years
    he's talking about adding many events to an element, not events to many elements
  • Ken Sherman
    Ken Sherman over 7 years
    That's a great resource -- thanks mmcrae, but still not what I want. i.e., it doesn't handle custom events. I'm wondering if javascript doesn't allow for what I'm looking for. (Also, i'd rather not require jquery if it's not a full solution)
  • Ken Sherman
    Ken Sherman over 7 years
    @pamblam thanks for the update -- but I did specify in the question that I don't want an array and foreach loop. that solution wouldn't handle custom events for instance
  • Don Cheadle
    Don Cheadle over 7 years
    What do you mean doesn't handle custom events? Then just add it to the string in on click mycustomevent mousedown?
  • Ken Sherman
    Ken Sherman over 7 years
    I can't edit my comment about not handling custom events. But you have to add each custom event manually. Again, I'm looking for a wildcard-type solution
  • I wrestled a bear once.
    I wrestled a bear once. over 7 years
    it will account for custom events if you add them to the loop. unfortunately i think this is your best bet.
  • Don Cheadle
    Don Cheadle over 7 years
    Seems like an odd requirement to have something bound to every possible event... but idk. Anyway, googling jquery wildcard event led to stuff you should probably check out such as stackoverflow.com/questions/9735608/…
  • Smilediver
    Smilediver over 3 years
    Shouldn't the test be /^on/ ? (starts with "on")
  • Smilediver
    Smilediver over 3 years
    Nice solution, although I prefer filter-map to reduce: .filter((prop) => prop.startsWith('on')).map((prop) => prop.substr(2))
  • Revircs
    Revircs over 3 years
    @SamHasler You are correct. It's been a while, but I believe I was using this with an event emitter at some point, so I just removed that part.
  • Revircs
    Revircs over 3 years
    An interesting thing I just noticed: Even without the /^on/, it doesn't seem to fire for GamePad events...
  • mplungjan
    mplungjan over 2 years
    Missing input, paste
  • jasonleonhard
    jasonleonhard over 2 years
    Copy paste that code in the DevTools to see this page has listeners you can list