假设你有这个div:
<div id="foo">bar</div>
你这样做:
$("#foo").click(someFunction);
Javascript 代码中的某个地方。
页面加载后,有没有办法通过 Firebug 或 Chrome 中的检查元素或其他任何方式找出
#foo
的点击事件是否绑定到 someFunction
?即,无需查看所有代码即可找到这一点?
Chrome 开发者工具可以做到这一点。
这将为您提供对象上的事件侦听器列表,可以扩展该列表以查找其源和附加函数。
Firebug 在 DOM 选项卡下有此信息,但用户不太友好。
您可以在浏览器内置开发者工具中使用控制台。它们是内置的 IE 和 Chrome,而 FF 则需要安装 Firebug AddOn。我不知道其他浏览器。
使用调试器控制台,您可以使用 jQuery
data("events")
查询元素的附加事件。此外,这些控制台还可以让您动态深入了解您感兴趣的事件的任何其他细节。
$("#foo").data("events");
在控制台中执行上述命令将显示一个对象,其中包含找到的每个事件的属性。在您的示例中,它返回一个具有
click
属性的对象,其类型为存储所有单击事件的数组。
如果您有单击事件并且您只需要该对象,您可以在控制台中执行以下操作:
$("#foo").data("events").click;
每个事件对象都有一个
handler
属性,它将向您显示它们绑定到的函数:
Object
data: null
guid: 2
handler: function mytestFunction(){
arguments: null
caller: null
guid: 2
length: 0
name: "mytestFunction"
prototype: mytestFunction
__proto__: function Empty() {}
namespace: ""
origType: "click"
quick: null
selector: null
type: "click"
__proto__: Object
参见DEMO,展示如何在控制台中查询和显示对象。
或者,您也可以使用“处理程序”作为整体摘要对象:
$("#foo").data("handlers");
请注意,
.data("events/handlers")
不会包含嵌入在 html 中的任何事件,如下所示:
<div id="foo" onclick="...">bar</div>
有关
data()
的更多信息位于 文档
我不了解 Firefox,但有一种简单的方法可以在 Chrome 和 Safari 中查看事件侦听器。只需打开开发人员工具,选择您的元素并滚动到 CSS 属性面板的底部。您会找到“事件侦听器”部分。
是的,这是可能的,你甚至可以在纯JS中做到这一点,但前提是你用自己的拦截器拦截/覆盖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());