【问题标题】:Getting an array of all DOM events possible获取所有可能的 DOM 事件的数组
【发布时间】:2012-03-11 05:32:32
【问题描述】:

我创建了一个多用途工厂事件发射器工厂函数。有了它,我可以将对象变成事件发射器。如果有人想看看或使用它,下面是事件发射器工厂的代码。

我的问题是如何从 DOM 中获取事件列表。 请注意,我不是要获取绑定事件的列表。我想要一个所有可能事件的列表。我想向发射器添加一个“管道”方法。此方法将获取一个 DOM 对象并绑定到所有可能的事件,然后当这些事件中的任何一个触发时,每个事件都会在发射器中触发一个同名事件。

我不认为有办法做到这一点。我准备制作一个硬编码的事件名称数组,但是如果我可以获取 DOM 的数组,那会更好,并且如果 W3C 标准化更多事件类型,它仍然可以工作。

附:如果你为 W3C 工作,这是一种让每个人都讨厌 DOM 的废话。请停止将 JavaScript 视为玩具语言。它不是一种玩具语言,它需要的不仅仅是玩具 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); });
        }
    }
}

【问题讨论】:

  • 这听起来很像我上个世纪的旧离散事件模型。由于 Firefox & Co 在 2K 之后不久在 IE 上的肮脏战争,我不得不将其关闭,因为动态构建所有支持的事件列表只能在 MSIE 上进行。我使用“at”而不是内置的“on”来区分事件模型。离散主要是因为它不会冒泡......

标签: javascript dom event-handling dom-events


【解决方案1】:

这是一个适用于 Chrome、Safari 和 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

这是适用于 IE9+、Chrome、Safari 和 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

这是一个使用较新 JavaScript 功能的版本([...new Set(...)] 用于过滤重复项,取代 filter 方法)。

[...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:结果是一系列事件名称,例如 ["onwebkitpointerlockerror", "onwebkitpointerlockchange", "onwebkitfullscreenerror", "onwebkitfullscreenchange", "onselectionchange", "onselectstart", "onsearch", "onreset", "onpaste", "onbeforepaste", "oncopy"] ... 等。

【讨论】:

  • 这是一个适用于所有这些的版本var eventList = []; for( var x in this )if( /\bon/.test(x) )eventList.push(x); "this" 即窗口通常可以用文档或任何其他感兴趣的元素替换。
【解决方案2】:

所有 DOM 事件都以 on 开头。您可以遍历任何Element 实例,并列出所有以on 开头的属性。

示例。在控制台中复制粘贴以下代码(Firefox,使用数组推导;)):

[i for(i in document)].filter(function(i){return i.substring(0,2)=='on'&&(document[i]==null||typeof document[i]=='function');})

另一种获取事件的方法是查看the specification,它显示:

  // 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;

【讨论】:

  • 注意。之前的事件列表不完整。例如,&lt;body&gt; element 还定义了一组事件。只需在规范中搜索 [TreatNonCallableAsNull] attribute Function? on 即可找到所有 (HTML5) 事件。
  • 我已经尝试循环遍历 DOM 级别 0 事件,但它们没有出现在 for in 循环中,因为 on* 方法在保留为 null 时是不可枚举的。请注意,我不是要捕获现有的绑定。我正在尝试获取可能绑定的动态列表。
  • @RobertHurst 在兼容的(现代)浏览器中,所有事件都是可枚举的。当它们尚未定义时,根据定义,它们必须是 null。由于事件是事先知道的,我建议创建一个事件列表并实施它。这比遍历许多元素的属性并过滤属性名称要高效得多。
  • Chrome 不同意。 for in 循环跳过所有 onevent 属性。看来我毕竟必须制作一个硬编码数组。不过我不想这样做,因为如果以后新标准引入了新的事件类型,我不想更新数组。
  • @Rocket 这是一个array comprehension,自 Firefox 2 中的 JavaScript 1.7 起可用。它也在 proposal list of ES-Harmony 上。
【解决方案3】:

这就是我在上个世纪获得动态eventList 的方式,当时为 IE 构建相当于为世界 87% 到 92% 的用户构建。它是一个单行器,例如 :: eventList = []; for( var x in this )if( x.match(/\bon/) )eventList.push(x);,我刚刚在我的 win 7 最新版 Opera、IE11、一个相当老的 Chrome 和大约 2 年的 Firefox 上对其进行了测试……该死(!) - 像魅力一样工作。

var eventList = [];

for( var x in this )if( /\bon/.test(x) )eventList.push(x),
console.info( x );

console.info( eventList.length );

【讨论】:

  • 对于那些想知道其他 50 多个事件在哪里丢失的人 - 它们正在打印并记录在 SO 控制台中! (?!承诺)。
【解决方案4】:

你不能有一个详尽的列表,因为我们可以用任何名称触发合成事件。

例如:

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

即使拥有所有“内置”事件的列表也几乎是不可能的。其中许多事件没有在任何地方公开.onevent IDL 属性,例如文档或窗口的DOMContentLoaded,或输入元素compositionXXX 或许多其他隐藏在各种规范中的元素。

// won't fire
window.onDOMContentLoaded = evt => console.log('caught through onDOMContentLoaded');
// will fire
window.addEventListener('DOMContentLoaded', evt => console.log('caught through addEventListener'));

即使捕获所有这些onevent IDL 属性也需要遍历所有构造函数的原型,因为window 只公开了其中的一部分。

console.log("onaddtrack available in window?", "onaddtrack" in window);
console.log("onaddtrack available in MediaStream's proto?", "onaddtrack" in MediaStream.prototype);

虽然我们可以在网上找到此类事件的quite big lists,但由于规范不断变化,浏览器不支持规范中的所有内容,或者相反支持规范中没有的功能,因此没有此类列表全部抓住

(例如,在最后一点上,dragexit event 目前正在从规范中删除,当时只有 Firefox 支持它。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-06
    • 2022-01-23
    • 1970-01-01
    • 2012-03-20
    • 1970-01-01
    • 2015-12-29
    相关资源
    最近更新 更多