Reputation: 9102
I've created a multi purpose factory event emitter factory function. With it I can turn objects into event emitters. The code for the event emitter factory is below if anyone would like to have a look or use it.
My question is how can I get a list of events from the DOM. Please note I'm not trying to get a list of bound events. I want a list of all events possible. I want to add a "pipe" method to emitters. This method would take a DOM object and bind to all possible events, then when any of those events fire each would trigger an event of the same name in the emitter.
I don't imagine there is a way to do this. I'm prepared to make a hard coded array of event names, but if I can get the array for the DOM instead that would be much better and would still work if the W3C standardizes more event types.
P.S. If you work for the W3C this is the kind of crap that makes everyone hate the DOM. Please stop treating JavaScript like a toy language. It is not a toy language and needs more than your toy DOM.
/**
* Creates a event emitter
*/
function EventEmitter() {
var api, callbacks;
//vars
api = {
"on": on,
"trigger": trigger
};
callbacks = {};
//return the api
return api;
/**
* Binds functions to events
* @param event
* @param callback
*/
function on(event, callback) {
var api;
if(typeof event !== 'string') { throw new Error('Cannot bind to event emitter. The passed event is not a string.'); }
if(typeof callback !== 'function') { throw new Error('Cannot bind to event emitter. The passed callback is not a function.'); }
//return the api
api = {
"clear": clear
};
//create the event namespace if it doesn't exist
if(!callbacks[event]) { callbacks[event] = []; }
//save the callback
callbacks[event].push(callback);
//return the api
return api;
function clear() {
var i;
if(callbacks[event]) {
i = callbacks[event].indexOf(callback);
callbacks[event].splice(i, 1);
if(callbacks[event].length < 1) {
delete callbacks[event];
}
return true;
}
return false;
}
}
/**
* Triggers a given event and optionally passes its handlers all additional parameters
* @param event
*/
function trigger(event ) {
var args;
if(typeof event !== 'string' && !Array.isArray(event)) { throw new Error('Cannot bind to event emitter. The passed event is not a string or an array.'); }
//get the arguments
args = Array.prototype.slice.apply(arguments).splice(1);
//handle event arrays
if(Array.isArray(event)) {
//for each event in the event array self invoke passing the arguments array
event.forEach(function(event) {
//add the event name to the begining of the arguments array
args.unshift(event);
//trigger the event
trigger.apply(this, args);
//shift off the event name
args.shift();
});
return;
}
//if the event has callbacks then execute them
if(callbacks[event]) {
//fire the callbacks
callbacks[event].forEach(function(callback) { callback.apply(this, args); });
}
}
}
Upvotes: 14
Views: 4884
Reputation: 137133
You can't have an exhaustive list, for one because we can fire synthetic events with any name.
For instance:
// Since we can even make it bubble
// overriding dispatchEvent wouldn't do either.
// Here we listen on document
document.addEventListener("foo", evt => console.log("foo fired"));
const event = new Event("foo", { bubbles: true });
// And we fire on <body>
document.body.dispatchEvent( event );
Even having a list of all "built-in" events is near impossible. Many of these events don't have an .onevent
IDL attribute exposed anywhere, like Document's or Window's DOMContentLoaded
, or inputs Elements compositionXXX
or many others hidden in various specs.
// won't fire
window.onDOMContentLoaded = evt => console.log('caught through onDOMContentLoaded');
// will fire
window.addEventListener('DOMContentLoaded', evt => console.log('caught through addEventListener'));
Even catching all these onevent
IDL attributes would require to walk through all the Constructor's prototypes since window
only exposes some of these.
console.log("onaddtrack available in window?", "onaddtrack" in window);
console.log("onaddtrack available in MediaStream's proto?", "onaddtrack" in MediaStream.prototype);
And while we can find quite big lists of such events online, since the specs are changing continuously and the browsers do not support everything from the specs, or at the contrary supporting features that are not in the specs, no such list will catch them all.
(For instance on this last point, the dragexit event is currently being removed from the specs, when only Firefox does support it.)
Upvotes: 0
Reputation: 5965
This is how I used to get the dynamic eventList
in the past century when building for IE was the equivalent of building for 87% to 92% of the world. It was a oneliner such as :: eventList = []; for( var x in this )if( x.match(/\bon/) )eventList.push(x);
and I just tested it on my win 7 up to date Opera, IE11, a pretty old Chrome, and about 2 years old Firefox... and damn (!) - worked like a charm.
var eventList = [];
for( var x in this )if( /\bon/.test(x) )eventList.push(x),
console.info( x );
console.info( eventList.length );
Upvotes: 0
Reputation: 3760
Here is a version that works in Chrome, Safari and FF.
Object.getOwnPropertyNames(document).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(Object.getPrototypeOf(document)))).filter(function(i){return !i.indexOf('on')&&(document[i]==null||typeof document[i]=='function');})
UPD 1:
And here is the version that works in IE9+, Chrome, Safari and FF.
Object.getOwnPropertyNames(document).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(Object.getPrototypeOf(document)))).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(window))).filter(function(i){return !i.indexOf('on')&&(document[i]==null||typeof document[i]=='function');}).filter(function(elem, pos, self){return self.indexOf(elem) == pos;})
UPD 2:
And here's a version using newer JavaScript features (the [...new Set(...)]
is to filter out duplicates, replacing the filter
approach).
[...new Set([
...Object.getOwnPropertyNames(document),
...Object.getOwnPropertyNames(Object.getPrototypeOf(Object.getPrototypeOf(document))),
...Object.getOwnPropertyNames(Object.getPrototypeOf(window)),
].filter(k => k.startsWith("on") && (document[k] == null || typeof document[k] == "function")))];
PS: the result is an array of events name like ["onwebkitpointerlockerror", "onwebkitpointerlockchange", "onwebkitfullscreenerror", "onwebkitfullscreenchange", "onselectionchange", "onselectstart", "onsearch", "onreset", "onpaste", "onbeforepaste", "oncopy"]
... ect.
Upvotes: 15
Reputation: 349222
All DOM events start with on
. You can loop through any Element
instance, and list all properties which start with on
.
Example. Copy-paste the following code in the console (Firefox, using Array comprehensions ;)):
[i for(i in document)].filter(function(i){return i.substring(0,2)=='on'&&(document[i]==null||typeof document[i]=='function');})
Another method to get the events is by looking at the specification, which reveals:
// event handler IDL attributes
[TreatNonCallableAsNull] attribute Function? onabort;
[TreatNonCallableAsNull] attribute Function? onblur;
[TreatNonCallableAsNull] attribute Function? oncanplay;
[TreatNonCallableAsNull] attribute Function? oncanplaythrough;
[TreatNonCallableAsNull] attribute Function? onchange;
[TreatNonCallableAsNull] attribute Function? onclick;
[TreatNonCallableAsNull] attribute Function? oncontextmenu;
[TreatNonCallableAsNull] attribute Function? oncuechange;
[TreatNonCallableAsNull] attribute Function? ondblclick;
[TreatNonCallableAsNull] attribute Function? ondrag;
[TreatNonCallableAsNull] attribute Function? ondragend;
[TreatNonCallableAsNull] attribute Function? ondragenter;
[TreatNonCallableAsNull] attribute Function? ondragleave;
[TreatNonCallableAsNull] attribute Function? ondragover;
[TreatNonCallableAsNull] attribute Function? ondragstart;
[TreatNonCallableAsNull] attribute Function? ondrop;
[TreatNonCallableAsNull] attribute Function? ondurationchange;
[TreatNonCallableAsNull] attribute Function? onemptied;
[TreatNonCallableAsNull] attribute Function? onended;
[TreatNonCallableAsNull] attribute Function? onerror;
[TreatNonCallableAsNull] attribute Function? onfocus;
[TreatNonCallableAsNull] attribute Function? oninput;
[TreatNonCallableAsNull] attribute Function? oninvalid;
[TreatNonCallableAsNull] attribute Function? onkeydown;
[TreatNonCallableAsNull] attribute Function? onkeypress;
[TreatNonCallableAsNull] attribute Function? onkeyup;
[TreatNonCallableAsNull] attribute Function? onload;
[TreatNonCallableAsNull] attribute Function? onloadeddata;
[TreatNonCallableAsNull] attribute Function? onloadedmetadata;
[TreatNonCallableAsNull] attribute Function? onloadstart;
[TreatNonCallableAsNull] attribute Function? onmousedown;
[TreatNonCallableAsNull] attribute Function? onmousemove;
[TreatNonCallableAsNull] attribute Function? onmouseout;
[TreatNonCallableAsNull] attribute Function? onmouseover;
[TreatNonCallableAsNull] attribute Function? onmouseup;
[TreatNonCallableAsNull] attribute Function? onmousewheel;
[TreatNonCallableAsNull] attribute Function? onpause;
[TreatNonCallableAsNull] attribute Function? onplay;
[TreatNonCallableAsNull] attribute Function? onplaying;
[TreatNonCallableAsNull] attribute Function? onprogress;
[TreatNonCallableAsNull] attribute Function? onratechange;
[TreatNonCallableAsNull] attribute Function? onreset;
[TreatNonCallableAsNull] attribute Function? onscroll;
[TreatNonCallableAsNull] attribute Function? onseeked;
[TreatNonCallableAsNull] attribute Function? onseeking;
[TreatNonCallableAsNull] attribute Function? onselect;
[TreatNonCallableAsNull] attribute Function? onshow;
[TreatNonCallableAsNull] attribute Function? onstalled;
[TreatNonCallableAsNull] attribute Function? onsubmit;
[TreatNonCallableAsNull] attribute Function? onsuspend;
[TreatNonCallableAsNull] attribute Function? ontimeupdate;
[TreatNonCallableAsNull] attribute Function? onvolumechange;
[TreatNonCallableAsNull] attribute Function? onwaiting;
// special event handler IDL attributes that only apply to Document objects
[TreatNonCallableAsNull,LenientThis] attribute Function? onreadystatechange;
Upvotes: 6