在JavaScript中收听所有事件

use*_*616 23 javascript

我正在试图弄清楚如何监听JavaScript对象上的所有事件.

我知道我可以用这样的东西添加个别事件

element.addEventListener("click", myFunction);
element.addEventListener("mouseover", myFunction);
...
Run Code Online (Sandbox Code Playgroud)

我想弄清楚是否有一个全能,我想做这样的事情:

// Begin pseudocode
var myObj = document.getElementById('someID');

myObj.addEventListener(/*catch all*/, myFunction);

function myFunction() {
  alert(/*event name*/);
}
// End pseudocode
Run Code Online (Sandbox Code Playgroud)

Rom*_*iev 24

要获取标准元素的事件.

var myObj = document.getElementById('someID');
for(var key in myObj){
    if(key.search('on') === 0) {
       myObj.addEventListener(key.slice(2), myFunction)
    }
}
Run Code Online (Sandbox Code Playgroud)

但正如@jeremywoertink所提到的,任何其他事件也是可能的.

  • @Olegzandr,窗口有什么问题?只需将“ myObj”替换为“ window”即可。但是,请注意,使用此方法,您还将监听一些不存在的事件,例如,如果存在全局变量“ onetwothree”,则上面的代码将为事件“ etwothree”添加一个监听器。(͡°͜ʖ͡°) (2认同)

rya*_*uen 17

@ roman-bekkiev答案的更现代的改写:

Object.keys(window).forEach(key => {
    if (/^on/.test(key)) {
        window.addEventListener(key.slice(2), event => {
            console.log(event);
        });
    }
});
Run Code Online (Sandbox Code Playgroud)

请注意,您可以进一步自定义要捕获的内容,例如:

/^on(key|mouse)/.test(key)

  • 请注意,由于箭头函数('=>'),这在 IE 上不起作用。如果你想在 IE 上使用 eventListener,请使用 Roman 的答案。 (2认同)

Cod*_*ody 5

我讨厌这个问题在没有原生或优雅的解决方案的情况下仍然存在。

更好的解决方案?

这允许您CustomEvent为任何EventTarget使用订阅单个target.addEventListener('*', ...)

    clear();

    /**
     * @param : source := EventTarget
     *  *   EventTarget.prototype
     *  *   Node (Element, Attr, etc)
     * @usage : [Node].addEventListener('*', ({ detail: e }) => {...}, false);
     */
    function proxyEventTargetSource(source) {
        var emit = source.dispatchEvent;  // obtain reference

        function proxy(event) {
            var { type } = event, any = new CustomEvent('*', { detail: event });  // use original event as detail
            if (!{ '*': true }[ type ]) emit.call(this, any);  // only emit "any" if type is not any.type ('*')
            return emit.call(this, event);
        }

        if ({ 'dispatchEvent': true }[ emit.name ]) source.dispatchEvent = proxy;  // attempt overwrite only if not already set (avoid rewrapping)
        return (source.dispatchEvent === proxy);  // indicate if its set after we try to
    }

    // proxyEventTargetSource(EventTarget.prototype);  // all targets
    proxyEventTargetSource(document);  // single target
    var e = new CustomEvent('any!', { detail: true });
    document.addEventListener('*', (e) => console.log('type: %s, original: %s, e: %O', e.type, e.detail.type, e), false);
    document.dispatchEvent(e);
Run Code Online (Sandbox Code Playgroud)

当然,更原生或更优雅的方式是使用原生Proxyonapply作为目标的dispatchEvent方法,但对于这篇文章来说,这可能会传达更少的信息。

要点:https : //gist.github.com/cScarlson/875a9fca7ab7084bb608fb66adff0463

已知的问题

显然,这仅在通过EventTargetsdispatchEvent方法驱动事件分派时有效。也就是说,通过鼠标事件(例如)自然触发事件是行不通的。需要有一种方法来包装由自然事件触发器调用的内部方法。

话虽如此,如果您有办法解决这个问题,请在另一个答案中展示您的想法。


pit*_*zle 5

据我所知,这是可能的。

\n
\n

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

\n
for (const key in target) {\n    if(/^on/.test(key)) {\n        const eventType = key.substr(2);\n        target.addEventListener(eventType, listener);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

据我所知,发出事件的唯一其他方式是 via EventTarget.dispatchEvent,其中 everyNode和fore everyElement继承。
\n要侦听所有这些手动触发的事件,我们可以dispatchEvent全局代理该方法,并为我们刚刚看到其名称的事件及时安装侦听器 \xe2\x9c\xa8 ^^

\n
const dispatchEvent_original = EventTarget.prototype.dispatchEvent;\nEventTarget.prototype.dispatchEvent = function (event) {\n    if (!alreadyListenedEventTypes.has(event.type)) {\n        target.addEventListener(event.type, listener, ...otherArguments);\n        alreadyListenedEventTypes.add(event.type);\n    }\n    dispatchEvent_original.apply(this, arguments);\n};\n
Run Code Online (Sandbox Code Playgroud)\n
\n

函数片段

\n
function addEventListenerAll(target, listener, ...otherArguments) {\n\n    // install listeners for all natively triggered events\n    for (const key in target) {\n        if (/^on/.test(key)) {\n            const eventType = key.substr(2);\n            target.addEventListener(eventType, listener, ...otherArguments);\n        }\n    }\n\n    // dynamically install listeners for all manually triggered events, just-in-time before they\'re dispatched ;D\n    const dispatchEvent_original = EventTarget.prototype.dispatchEvent;\n    function dispatchEvent(event) {\n        target.addEventListener(event.type, listener, ...otherArguments);  // multiple identical listeners are automatically discarded\n        dispatchEvent_original.apply(this, arguments);\n    }\n    EventTarget.prototype.dispatchEvent = dispatchEvent;\n    if (EventTarget.prototype.dispatchEvent !== dispatchEvent) throw new Error(`Browser is smarter than you think!`);\n\n}\n\n\n// usage example\naddEventListenerAll(window, (evt) => {\n    console.log(evt.type);\n});\ndocument.body.click();\ndocument.body.dispatchEvent(new Event(\'omg!\', { bubbles: true }));\n\n\n// usage example with `useCapture`\n// (also receives `bubbles: false` events, but in reverse order)\naddEventListenerAll(\n    window,\n    (evt) => { console.log(evt.type); },\n    true\n);\ndocument.body.dispatchEvent(new Event(\'omfggg!\', { bubbles: false }));\n
Run Code Online (Sandbox Code Playgroud)\n


jer*_*ink 2

您可以使用EventEmitter2来处理通配符。像您所说的那样进行包罗万象的问题是有太多事件,您可以创建自己的事件。您必须专门创建一个您正在谈论的事件的数组,对其进行迭代,然后单独绑定每个事件。