我正在寻找一种方法来列出由 Javascript 添加到某个元素的所有事件。我知道
getEventListeners
在开发工具中可用,但在脚本中不可用。
我知道使用 jQuery,我可以使用下面的代码检查事件。然而,由于这是一个私有方法,它只能列出 jQuery 添加的事件。
$._data(element, 'events')
我也检查了这个页面,但是这个方法需要在添加事件之前执行。 https://www.sqlpac.com/en/documents/javascript-listing-active-event-listeners.html
毕竟,有没有通用的方法可以在添加事件后检查元素是否有事件?
这是可能的,但前提是您使用自己的拦截器拦截/覆盖 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());