60

First of all, here is a list of event types that are defined by the W3C standards. (This list is based on the onevent attributes defined in the HTML5 standard. I assume that there are dozens of other event types, but this list is long enough as it is.)

  • abort
  • afterprint
  • beforeprint
  • beforeunload
  • blur
  • canplay
  • canplaythrough
  • change
  • click
  • contextmenu
  • copy
  • cuechange
  • cut
  • dblclick
  • DOMContentLoaded
  • drag
  • dragend
  • dragenter
  • dragleave
  • dragover
  • dragstart
  • drop
  • durationchange
  • emptied
  • ended
  • error
  • focus
  • focusin
  • focusout
  • formchange
  • forminput
  • hashchange
  • input
  • invalid
  • keydown
  • keypress
  • keyup
  • load
  • loadeddata
  • loadedmetadata
  • loadstart
  • message
  • mousedown
  • mouseenter
  • mouseleave
  • mousemove
  • mouseout
  • mouseover
  • mouseup
  • mousewheel
  • offline
  • online
  • pagehide
  • pageshow
  • paste
  • pause
  • play
  • playing
  • popstate
  • progress
  • ratechange
  • readystatechange
  • redo
  • reset
  • resize
  • scroll
  • seeked
  • seeking
  • select
  • show
  • stalled
  • storage
  • submit
  • suspend
  • timeupdate
  • undo
  • unload
  • volumechange
  • waiting

Now, is it possible to define a global event handler that is called when any event originally occurs on any element on the page? (In this case, I don't want to count those events that occurred on elements because they bubbled up from a descendant element - that's why I wrote "originally occurs".)

If that is not possible, is it at least possible to define an event handler that is called when any event bubbles up to the root of the DOM tree (which is either the document object or the window object - both should work)? (I know that it's possible to stop bubbling programmatically, but I would use this event handler on a page that has no other handlers defined on any other elements.) (Also, I believe some events don't bubble up, but let's ignore these cases for the sake of this argument.)

I know that I can do this (using jQuery):

$(document).bind('abort afterprint beforeprint beforeunload etc.', function() {
    // handle event
});

but that would be a rather undesirable solution for me.

btw I don't need a cross-browser solution. If it works in just one browser, I'm fine.

Also, Firebug is able to log events, but I would like to be able to catch the event programmatically (via JavaScript) rather then having them simply logged in the console.

Community
  • 1
  • 1
Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • 3
    You've already got that nice list of event types; why not just use `$('body').delegate('*', allEventTypes.join(' '), function() { ... })` – Pointy Feb 24 '11 at 16:25
  • @Pointy That would be Plan B. But if at least one browser exposes the functionality to catch all events that bubble up to the root, I would like to know about that feature. – Šime Vidas Feb 24 '11 at 16:55
  • 1
    @Pointy Although binding to the window object is fine too (no need for delegate): `$(window).bind('all those event types', function() { ... });` – Šime Vidas Feb 24 '11 at 17:50
  • oh yes I guess you're right - ".delegate('*', ...)" is kind-of silly :-) – Pointy Feb 24 '11 at 18:00
  • How come I didn't find the "DOMNodeInserted" event (a rather important event since my library I am building now **depends** on it) in your magnificent list? – Steven Lu Jan 06 '13 at 05:17
  • @StevenLu Mutation events are deprecated, and considered flawed. The current agenda is to replace them with [mutation observers](https://developer.mozilla.org/en-US/docs/DOM/MutationObserver). – Šime Vidas Jan 06 '13 at 14:33
  • Yes, in the hours following my comment I learned all about mutation observers. – Steven Lu Jan 06 '13 at 17:38
  • possible duplicate of [Listen to all javascript events](http://stackoverflow.com/questions/3489433/listen-to-all-javascript-events) – Liam Nov 08 '13 at 10:29
  • Possible duplicate of [How do you log all events fired by an element in jQuery?](http://stackoverflow.com/questions/7439570/how-do-you-log-all-events-fired-by-an-element-in-jquery) – Günter Zöchbauer Dec 09 '15 at 09:26

8 Answers8

28
/*

function getAllEventTypes(){

  if(location.href !='https://developer.mozilla.org/en-US/docs/Web/Events') return;

  var types = {};
  $('.standard-table:eq(0) tr').find('td:eq(1)').map(function(){
    var type = $.trim(this.innerText) || 'OtherEvent';
    types[type] = types[type] || [];     
    var event = $.trim(this.previousElementSibling.innerText);
    if(event) types[type].push(event);
  });
  for(var t in types) types[t] = types[t].join(' ');
  return "var DOMEvents = "+JSON.stringify(types, null, 4).replace(/"(\w+)\":/ig, '$1:');
}

*/

var DOMEvents = {
UIEvent: "abort DOMActivate error load resize scroll select unload",
ProgressEvent: "abort error load loadend loadstart progress progress timeout",
Event: "abort afterprint beforeprint cached canplay canplaythrough change chargingchange chargingtimechange checking close dischargingtimechange DOMContentLoaded downloading durationchange emptied ended ended error error error error fullscreenchange fullscreenerror input invalid languagechange levelchange loadeddata loadedmetadata noupdate obsolete offline online open open orientationchange pause pointerlockchange pointerlockerror play playing ratechange readystatechange reset seeked seeking stalled submit success suspend timeupdate updateready visibilitychange volumechange waiting",
AnimationEvent: "animationend animationiteration animationstart",
AudioProcessingEvent: "audioprocess",
BeforeUnloadEvent: "beforeunload",
TimeEvent: "beginEvent endEvent repeatEvent",
OtherEvent: "blocked complete upgradeneeded versionchange",
FocusEvent: "blur DOMFocusIn  Unimplemented DOMFocusOut  Unimplemented focus focusin focusout",
MouseEvent: "click contextmenu dblclick mousedown mouseenter mouseleave mousemove mouseout mouseover mouseup show",
SensorEvent: "compassneedscalibration Unimplemented userproximity",
OfflineAudioCompletionEvent: "complete",
CompositionEvent: "compositionend compositionstart compositionupdate",
ClipboardEvent: "copy cut paste",
DeviceLightEvent: "devicelight",
DeviceMotionEvent: "devicemotion",
DeviceOrientationEvent: "deviceorientation",
DeviceProximityEvent: "deviceproximity",
MutationNameEvent: "DOMAttributeNameChanged DOMElementNameChanged",
MutationEvent: "DOMAttrModified DOMCharacterDataModified DOMNodeInserted DOMNodeInsertedIntoDocument DOMNodeRemoved DOMNodeRemovedFromDocument DOMSubtreeModified",
DragEvent: "drag dragend dragenter dragleave dragover dragstart drop",
GamepadEvent: "gamepadconnected gamepaddisconnected",
HashChangeEvent: "hashchange",
KeyboardEvent: "keydown keypress keyup",
MessageEvent: "message message message message",
PageTransitionEvent: "pagehide pageshow",
PopStateEvent: "popstate",
StorageEvent: "storage",
SVGEvent: "SVGAbort SVGError SVGLoad SVGResize SVGScroll SVGUnload",
SVGZoomEvent: "SVGZoom",
TouchEvent: "touchcancel touchend touchenter touchleave touchmove touchstart",
TransitionEvent: "transitionend",
WheelEvent: "wheel"
}

var RecentlyLoggedDOMEventTypes = {};

for (var DOMEvent in DOMEvents) {

  var DOMEventTypes = DOMEvents[DOMEvent].split(' ');

  DOMEventTypes.filter(function(DOMEventType) {
    var DOMEventCategory = DOMEvent + ' ' + DOMEventType;  
    document.addEventListener(DOMEventType, function(e){
      if(RecentlyLoggedDOMEventTypes[DOMEventCategory]) { return; }
      RecentlyLoggedDOMEventTypes[DOMEventCategory] = true;
      setTimeout(function(){ RecentlyLoggedDOMEventTypes[DOMEventCategory] = false }, 5000);
      var isActive = e.target == document.activeElement;
      if(isActive) {
        console.info(DOMEventCategory, 
          ' target=', e.target, 
          ' active=', document.activeElement, 
          ' isActive=', true );
      } else {
        console.log(DOMEventCategory, 
          ' target=', e.target,
          ' active=', document.activeElement, 
          ' isActive=', false );
      }

    }, true);
  });

}
Jez
  • 27,951
  • 32
  • 136
  • 233
Vlad Mysla
  • 1,181
  • 12
  • 15
  • 1
    Please see my answer for an updated version of getAllEventTypes that is working on the latest version of the MDN website. – Martino Lessio Aug 28 '17 at 14:05
16

You can iterate through all properties of dom element and select ones that match /on(.*)/ pattern (for example onclick or onmousemove):

var events = [];
for (var property in element) {
    var match = property.match(/^on(.*)/)
    if (match) { 
        events.push(match[1]);
    }
}
console.log(events.join(' '))
YankovskyAndrey
  • 310
  • 3
  • 7
  • 1
    This one worth a try guys. The none-complicated solution. But I still suggest `console.log(events);` without `join()` cause it makes it more readable. – Ahmad Maleki Oct 18 '17 at 08:43
6

How to listen for all events on a specific target Element


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
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 }));
pitizzzle
  • 303
  • 3
  • 8
5

I highly doubt there's a way to do this in Firefox. Looking at Firebug's source code (particularly the attachAllListeners method), turns out that iterating through a list of event names is obviously the way to go, but this doesn't solve the bubbling issues.

Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138
user123444555621
  • 148,182
  • 27
  • 114
  • 126
3

There doesn't seem to be any 'easy-way' to do that.

My idea: You know which are all the events, so you can handle all events for every DOM element:

var events =
[   
    "onabort",
    "onafterprint",
    "onbeforeprint",
    "onbeforeunload",
    ...

];

var root = document.body;
var elms = root.childNodes;

for(var i = 0; i < elms.length; i++)
{
    for(var j = 0; j < events.length; j++)
    {
        elms[i][events[j]] = globalHandler;
    }
}

function globalHandler()
{
    alert("Global handler called");
}

That's the 'intuitive idea' but doesn't seem to be very efficient. However, it should work.

Good luck.

josec89
  • 1,932
  • 1
  • 16
  • 19
  • 3
    I don't think that assigning an event handler for every possible event type to every possible element on the page is a good idea. I fear that my browser would explode `:)` – Šime Vidas Feb 24 '11 at 21:56
  • 4
    @Sime Just make sure you only have a few elements and you're good to go! – Prescott Feb 25 '11 at 03:15
1

A bit late to the party but I did create something that might be useful for others here.

https://codepen.io/phreaknation/pen/QmJjEa

This is an ES6 Class that captures all events from an element that is known to that element. This demo allows you to change the element time in the page, as well as read out the events with clickable links to their MDN page as well as interact with the element and see how the events are triggered with time stamps.

I hope this helps

Class code

class EventSystem {
  constructor(element) {
    this._ = {
      element: null
    }

    return this;
  }

  getAllEventTypes({blacklist = [], whitelist = []} = {}) {
    const events = [];
    for (let property in this._.element) {
      const match = property.match(/^on(.*)/);
      if (match) {
        if ((whitelist.length > 0 ? whitelist.indexOf(match) !== -1 : true) &&
            (blacklist.length > 0 ? blacklist.indexOf(match) === -1 : true)) {
          events.push(match[1]);
        }          
      }
    }
    return events;
  }

  getElementType() {
    return this._.element.tagName.toLowerCase();
  }

  setElement(element) {
    this._.element = element;
    return this;
  }

  applyEvents(events, callback) {
    events.forEach((event) => {
      this._.element.addEventListener(event, (ev) => {
        if (typeof callback === 'function') {
          callback(event, ev);
        }
      })
    })
  }
}
Phreak Nation
  • 860
  • 7
  • 10
1

My solution to this problem. I loop through all datatypes on the global context (window, in this case), check if the type extends EventTarget, and then extracts them via checking for the "on" prefix.

const getEventNames = (root) => {
  let events = [ ];

  const objectHasSubPrototype = (object, comp) => {
    let proto = Object.getPrototypeOf(object);

    while(proto !== null && proto !== EventTarget) {
      proto = Object.getPrototypeOf(proto);
    }

    return (proto !== null);
  };

  const addEventNames = (propNames) => {
    propNames.filter(x => x.match(/^on\w+$/)).forEach((propName) => {
      propName = propName.substr(2);
      if(events.indexOf(propName) === -1) {
        events.push(propName);
      }
    });
  };

  Object.getOwnPropertyNames(root).forEach((name) => {
    let value = root[name];

    if(value) {
      if(objectHasSubPrototype(value, EventTarget)) {
        let propNames = Object.getOwnPropertyNames(Object.getPrototypeOf(value).prototype);
        addEventNames(propNames);

        propNames = Object.getOwnPropertyNames(window);
        addEventNames(propNames);
      }
    }
  });

  return events;
};

// Attach all events to the window
getEventNames(window).forEach((eventName) => {
  window.addEventListener(eventName, (event) => console.log(eventName, event));
});
Hayley
  • 2,977
  • 3
  • 18
  • 16
-2

For the last version of the MDN website:

(function getAllEventTypes(){
  if(location.href !='https://developer.mozilla.org/en-US/docs/Web/Events') return;

  var types = {};
  $('.standard-table').map(function(){
    if($(this).find('caption').length > 0){
        var type = $(this).find('caption')[0].innerHTML || 'OtherEvent';
    types[type] = types[type] || [];     
    $(this).find('tbody tr td code a').each(function(el){
        if(this.innerText) types[type].push(this.innerText);
    });
    }
  });
  for(var t in types) types[t] = types[t].join(' ');
  return "var DOMEvents = "+JSON.stringify(types, null, 4).replace(/"(\w+)\":/ig, '$1:');
})();
Martino Lessio
  • 775
  • 1
  • 9
  • 17
  • 4
    I do not feel this is a good answer. The first line is a url check to see if the current page is a specific page or not. The rest of the code looks in the current page for a table which if someone used this on their page, would not exist. I tried running this code locally and it would never execute, and fail when I commented out the first line. This is not a valid solution. This boils down to only being able to work on the MDN events page, only. – Phreak Nation Apr 06 '18 at 15:56
  • 1
    @PhreakNation the function is to be run on that url to generate the code... not that its good but its not supposed to be stand alone – Carter Cole Sep 05 '18 at 17:41
  • Why return code that you must eval? – code Mar 26 '22 at 17:58