Javascript 是否可以在浏览器中以编程方式捕获页面上的所有事件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5107232/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 15:41:27  来源:igfitidea点击:

Is it possible to programmatically catch all events on the page in the browser?

javascripteventsdombrowser

提问by ?ime Vidas

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.)

首先,这里列出了 W3C 标准定义的事件类型。(此列表基于 HTML5 标准中定义的 onevent 属性。我假设还有数十种其他事件类型,但此列表已足够长。)

  • 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
  • 中止
  • 印后
  • 印前
  • 卸载前
  • 模糊
  • 可以玩
  • 可通关
  • 改变
  • 点击
  • 上下文菜单
  • 复制
  • 提示改变
  • 双击
  • 已加载 DOM 内容
  • 拖尾
  • 拖拉机
  • 拖拽离开
  • 拖拽
  • 拖拽启动
  • 降低
  • 持续时间变化
  • 清空
  • 结束
  • 错误
  • 重点
  • 专注于
  • 聚焦
  • 形式变化
  • 表单输入
  • 哈希更改
  • 输入
  • 无效的
  • 按键
  • 按键
  • 按键
  • 加载
  • 加载数据
  • 加载元数据
  • 加载启动
  • 信息
  • 鼠标按下
  • 鼠标输入
  • 鼠标离开
  • 鼠标移动
  • 鼠标移出
  • 鼠标移到
  • 鼠标向上
  • 鼠标滚轮
  • 离线
  • 在线的
  • 页面隐藏
  • 页面展示
  • 粘贴
  • 暂停
  • 弹出状态
  • 进步
  • 利率变化
  • 准备状态改变
  • 重做
  • 重启
  • 调整大小
  • 滚动
  • 寻求
  • 寻求
  • 选择
  • 展示
  • 停滞不前
  • 贮存
  • 提交
  • 暂停
  • 时间更新
  • 撤销
  • 卸下
  • 体积变化
  • 等待

Now, is it possible to define a global event handler that is called when anyevent originally occurs on anyelement 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 anyevent bubbles up to the root of the DOM tree (which is either the documentobject or the windowobject - 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.)

如果这是不可能的,是否至少可以定义一个事件处理程序,当任何事件冒泡到 DOM 树的根部(即document对象或window对象 - 两者都应该工作)时调用该处理程序?(我知道有可能以编程方式停止冒泡,但我会在没有在任何其他元素上定义其他处理程序的页面上使用此事件处理程序。)(此外,我相信某些事件不会冒泡,但让我们忽略这些为了这个论点的情况。)

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

我知道我可以做到这一点(使用 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.

此外,Firebug 能够记录事件,但我希望能够以编程方式(通过 JavaScript)捕获事件,而不是让它们简单地登录到控制台。

回答by Vlad Mysla

/*

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, ':');
}

*/

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(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);
  });

}

回答by YankovskyAndrey

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

您可以遍历 dom 元素的所有属性并选择匹配 /on(.*)/ 模式的属性(例如 onclick 或 onmousemove):

var events = [];
for (var property in element) {
    var match = property.match(/^on(.*)/)
    if (match) { 
        events.push(match[1]);
    }
}
console.log(events.join(' '))

回答by user123444555621

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

我非常怀疑在 Firefox 中有一种方法可以做到这一点。查看Firebug 的源代码(特别是attachAllListeners方法),结果证明遍历事件名称列表显然是可行的方法,但这并不能解决冒泡问题。

回答by josec89

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:

我的想法:您知道哪些是所有事件,因此您可以处理每个 DOM 元素的所有事件:

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.

祝你好运。

回答by Phreak Nation

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

聚会有点晚了,但我确实创造了一些可能对其他人有用的东西。

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

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.

这是一个 ES6 类,它从该元素已知的元素中捕获所有事件。该演示允许您更改页面中的元素时间,以及通过指向其 MDN 页面的可点击链接读出事件,以及与元素交互并查看事件如何通过时间戳触发。

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);
        }
      })
    })
  }
}

回答by yuikonnu

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.

我对这个问题的解决方案。我遍历全局上下文中的所有数据类型(window在本例中为 ),检查类型是否扩展EventTarget,然后通过检查“ on”前缀来提取它们。

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));
});

回答by Martino Lessio

For the last version of the MDN website:

对于 MDN 网站的最新版本:

(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, ':');
})();