Robert Hurst
Robert Hurst

Reputation: 9102

Getting an array of all DOM events possible

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

Answers (4)

Kaiido
Kaiido

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

Bekim Bacaj
Bekim Bacaj

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

obenjiro
obenjiro

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

Rob W
Rob W

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

Related Questions