如何通过Javascript检查绑定到元素的事件列表

问题描述 投票:0回答:1

我正在寻找一种方法来列出由 Javascript 添加到某个元素的所有事件。我知道

getEventListeners
在开发工具中可用,但在脚本中不可用。

我知道使用 jQuery,我可以使用下面的代码检查事件。然而,由于这是一个私有方法,它只能列出 jQuery 添加的事件。

$._data(element, 'events')

我也检查了这个页面,但是这个方法需要在添加事件之前执行。 https://www.sqlpac.com/en/documents/javascript-listing-active-event-listeners.html

毕竟,有没有通用的方法可以在添加事件后检查元素是否有事件?

javascript jquery-events
1个回答
0
投票

这是可能的,但前提是您使用自己的拦截器拦截/覆盖 addEventListener 和 removeEventListener 原型函数,这样您就可以拦截它们。

这适用于使用 addEventListener 添加的任何内容(并且它考虑了 removeEventListener)。

但是如果您在没有 EventListener 的情况下添加它们,例如使用 element.onclick (或在标记中的 onclick/onAnything-attribute 中),这不会列出它们,您必须手动检查它们。

确保以下 JavaScript 是您页面上执行的第一个脚本,否则可能无法正常工作。

方法如下(TypeScript):

type EventHandlerMapType = {
    // [key: EventTarget]: { [type: string]: EventListenerOrEventListenerObject[] };
    [key: string]: { [type: string]: EventListenerOrEventListenerObject[] };
};


type EventHandlerMapValue = { [type: string]: EventListenerOrEventListenerObject[] };
interface EventTarget
{
    getEventHandlers: (type?: string) => EventHandlerMapValue | EventListenerOrEventListenerObject[];
}





// function addEventListener<K extends keyof ElementEventMap>(type: K, listener: (this: Element, ev: ElementEventMap[K]) => any, options ?: boolean | AddEventListenerOptions): void;
// addEventListener(type: string, listener: EventListenerOrEventListenerObject, options ?: boolean | AddEventListenerOptions): void;


(function ()
{
    // Store the handlers by element reference
    // WeakMap can take an object, such as an Element, as a key, object cannot. 
    // This is useful because WeakMap allows for garbage collection of the keys(the elements), 
    // meaning when an Element is removed from the DOM and no longer referenced, it gets garbage - collected, 
    // and its entry in the WeakMap is automatically removed. 
    // This prevents memory leaks.
    const eventHandlerMap = new WeakMap<EventTarget>(); // Dictionary<Element, { type:[]}> // where type is string and array is an array of handlers/listeners

    // Override the native addEventListener
    const originalAddEventListener = EventTarget.prototype.addEventListener;

    
    
    EventTarget.prototype.addEventListener = function (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions)
    {
        // Call the original addEventListener to ensure normal behavior
        originalAddEventListener.call(this, type, listener, options);

        // Initialize tracking for the current element if it doesn't exist
        if (!eventHandlerMap.has(this))
        {
            eventHandlerMap.set(this, {});
        }

        // Get the event type handlers for this element
        const handlersForElement = eventHandlerMap.get(this);
        if (!handlersForElement[type])
        {
            handlersForElement[type] = [];
        }

        // Add the handler to the list for this event type
        handlersForElement[type].push(listener);
    };

    // Override the native removeEventListener
    const originalRemoveEventListener = EventTarget.prototype.removeEventListener;
    EventTarget.prototype.removeEventListener = function (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions)
    {
        // Call the original removeEventListener to ensure normal behavior
        originalRemoveEventListener.call(this, type, listener, options);

        // Remove the handler from the tracking list
        if (eventHandlerMap.has(this))
        {
            const handlersForElement = eventHandlerMap.get(this);
            if (handlersForElement[type])
            {
                // Filter out the handler that matches the one being removed
                handlersForElement[type] = handlersForElement[type].filter((h: EventListenerOrEventListenerObject) => h !== listener);

                // Clean up if no handlers left for this event type
                if (handlersForElement[type].length === 0)
                {
                    delete handlersForElement[type];
                }
            }

            // Clean up the element if no handlers left for any event type
            if (Object.keys(handlersForElement).length === 0)
            {
                eventHandlerMap.delete(this);
            }
        }
    };

    // Function to retrieve all event handlers for an element
    EventTarget.prototype.getEventHandlers = function (type?: string): EventHandlerMapValue | EventListenerOrEventListenerObject[]
    {
        // Get the tracking list for the current element
        const handlersForElement = eventHandlerMap.get(this) || {};

        if (type)
        {
            // If a specific event type is requested, return its handlers
            return handlersForElement[type] || [];
        }

        // If no type is specified, return all handlers grouped by type
        return handlersForElement;
    };

})();

现在在 EventTarget(元素、节点等)上:

getEventHandlers(type?: string)

或者用普通的 JS

(function () {
    var eventHandlerMap = new WeakMap();
    var originalAddEventListener = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function (type, listener, options) {
        originalAddEventListener.call(this, type, listener, options);
        if (!eventHandlerMap.has(this)) {
            eventHandlerMap.set(this, {});
        }
        var handlersForElement = eventHandlerMap.get(this);
        if (!handlersForElement[type]) {
            handlersForElement[type] = [];
        }
        handlersForElement[type].push(listener);
    };
    var originalRemoveEventListener = EventTarget.prototype.removeEventListener;
    EventTarget.prototype.removeEventListener = function (type, listener, options) {
        originalRemoveEventListener.call(this, type, listener, options);
        if (eventHandlerMap.has(this)) {
            var handlersForElement = eventHandlerMap.get(this);
            if (handlersForElement[type]) {
                handlersForElement[type] = handlersForElement[type].filter(function (h) { return h !== listener; });
                if (handlersForElement[type].length === 0) {
                    delete handlersForElement[type];
                }
            }
            if (Object.keys(handlersForElement).length === 0) {
                eventHandlerMap.delete(this);
            }
        }
    };
    EventTarget.prototype.getEventHandlers = function (type) {
        var handlersForElement = eventHandlerMap.get(this) || {};
        if (type) {
            return handlersForElement[type] || [];
        }
        return handlersForElement;
    };
})();

测试:

var btnCreated = document.createElement("button");
btnCreated.textContent = "Hello Kitty";
btnCreated.value = "Hello Kitty";
document.body.appendChild(btnCreated);
var btn = document.querySelector('button');
function handleClick() {
    console.log('Button clicked');
}
btn.addEventListener('click', handleClick);
btn.addEventListener('clock', handleClick);
console.log(btn.getEventHandlers('click'));
console.log("before click");
btn.click();
console.log("after click");
btn.removeEventListener('click', handleClick);
console.log("before click after click removed");
btn.click();
console.log("after click after click removed");
console.log("click handlers", btn.getEventHandlers('click'));
console.log("all handlers", btn.getEventHandlers());
© www.soinside.com 2019 - 2024. All rights reserved.