Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 463
Ken Sherman Avatar asked Oct 10 '16 17:10

Ken Sherman


People also ask

Can I add an event listener to multiple elements in JavaScript?

Adding an Event Listener to Multiple Elements Using a for...of Loop + IIFE. Open the console and click any of the buttons. The same event listener is added to each button using a for...of loop along with an immediately invoked function that passes the current element of the loop back into the function.

Can you add an event listener to a query selector all?

To add an event listener to the results from the querySelectorAll method: Use the forEach() method to iterate over the collection of elements. Call the addEventListener() method on each element in the collection.

How do you list all event listeners?

Right-click on the search icon button and choose “inspect” to open the Chrome developer tools. Once the dev tools are open, switch to the “Event Listeners” tab and you will see all the event listeners bound to the element. You can expand any event listener by clicking the right-pointing arrowhead.


2 Answers

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.

like image 154
wilhelmpaulm Avatar answered Oct 21 '22 18:10

wilhelmpaulm


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 }));
like image 42
pitizzzle Avatar answered Oct 21 '22 20:10

pitizzzle