How can I add an event listener for all events in javascript without listing them individually?
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);
}
Ken Sherman
Updated on August 07, 2021Comments
-
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 over 7 yearsI 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 over 7 yearshe's talking about adding many events to an element, not events to many elements
-
Ken Sherman over 7 yearsThat'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 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 over 7 yearsWhat do you mean doesn't handle custom events? Then just add it to the string in on
click mycustomevent mousedown
? -
Ken Sherman over 7 yearsI 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. over 7 yearsit will account for custom events if you add them to the loop. unfortunately i think this is your best bet.
-
Don Cheadle over 7 yearsSeems 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 over 3 yearsShouldn't the test be
/^on/
? (starts with "on") -
Smilediver over 3 yearsNice solution, although I prefer filter-map to reduce:
.filter((prop) => prop.startsWith('on')).map((prop) => prop.substr(2))
-
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 over 3 yearsAn interesting thing I just noticed: Even without the /^on/, it doesn't seem to fire for GamePad events...
-
mplungjan over 2 yearsMissing input, paste
-
jasonleonhard over 2 yearsCopy paste that code in the DevTools to see this page has listeners you can list