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

时间:2011-02-24 16:09:40

标签: javascript events dom browser

首先,这里列出了W3C标准定义的事件类型。 (此列表基于HTML5标准中定义的一个属性。我假设有许多其他事件类型,但这个列表足够长。)

  • 中止
  • 打印后
  • beforeprint
  • beforeunload
  • 模糊
  • canplay
  • canplaythrough
  • 变化
  • 点击
  • 文本菜单
  • 复制
  • cuechange
  • DBLCLICK
  • DOMContentLoaded
  • dragend
  • 的dragenter
  • dragleave
  • 的dragover
  • 的dragstart
  • durationchange
  • 结束
  • 错误
  • 焦点
  • 的focusIn
  • 事件的内容
  • formchange
  • 的formInput
  • hashchange
  • 输入
  • 无效
  • KEYDOWN
  • 按键
  • KEYUP
  • 负载
  • loadeddata
  • 等待loadedmetadata
  • loadstart
  • 消息
  • 鼠标按下
  • 的mouseenter
  • 鼠标离开
  • 鼠标移动
  • 鼠标移开
  • 鼠标悬停
  • 鼠标松开
  • 滚轮
  • 离线
  • 在线
  • pagehide
  • pageshow
  • 暂停
  • 播放
  • popstate
  • 进步
  • ratechange
  • readystatechange
  • 重做
  • 重置
  • 调整大小
  • 滚动
  • seeked
  • 选择
  • 显示
  • 停滞
  • 存储
  • 提交
  • 暂停
  • timeupdate
  • 撤消
  • 卸载
  • volumechange
  • 等待

现在,是否有可能定义一个全局事件处理程序,当任何事件最初发生在页面上的任何元素时调用该事件处理程序? (在这种情况下,我不想计算那些在元素上发生的事件,因为它们从后代元素冒出来 - 这就是我写“最初发生”的原因。)

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

我知道我可以这样做(使用jQuery):

window

但这对我来说是一个非常不理想的解决方案。

btw我不需要跨浏览器解决方案。如果只在一个浏览器中工作,我很好。

此外,Firebug is able to log events,但我希望能够以编程方式(通过JavaScript)捕获事件,而不是让它们只是登录控制台。

8 个答案:

答案 0 :(得分:20)

/*

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

}

答案 1 :(得分:8)

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

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

答案 2 :(得分:5)

我非常怀疑在Firefox中有一种方法可以做到这一点。查看Firebug's source code(特别是attachAllListeners方法),结果显示迭代事件名称列表显然是要走的路,但这并不能解决冒泡问题。

答案 3 :(得分:2)

似乎没有任何“简单方法”可以做到这一点。

我的想法:  您知道哪些是所有事件,因此您可以处理每个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");
}

这是'直观的想法',但似乎效率不高。但是,它应该有效。

祝你好运。

答案 4 :(得分:0)

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

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

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

我希望这对您有帮助

类代码

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

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

如何监听特定目标 Element 上的所有事件 ?


对于所有原生事件,我们可以通过迭代 target.onevent 属性并为所有这些属性安装我们的侦听器来检索支持的事件列表。

for (const key in target) {
    if(/^on/.test(key)) {
        const eventType = key.substr(2);
        target.addEventListener(eventType, listener);
    }
}

据我所知,发出事件的唯一另一种方式是通过 EventTarget.dispatchEvent,每个 Node 和之前的每个 Element 都继承。
要侦听所有这些手动触发的事件,我们可以全局代理 dispatchEvent 方法,并为我们刚刚看到名称的事件及时安装我们的侦听器 ✨ ^^

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

答案 7 :(得分:-1)

对于最新版本的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, '$1:');
})();