【发布时间】:2011-02-07 01:31:50
【问题描述】:
有什么方法可以查看 DOM 元素的任何事件附加了哪些函数/代码?使用 Firebug 或任何其他工具。
【问题讨论】:
-
使用 jQuery 或使用原生 DOM 附加?
-
@SLaks:好问题。使用任何机制附加
标签: javascript events
有什么方法可以查看 DOM 元素的任何事件附加了哪些函数/代码?使用 Firebug 或任何其他工具。
【问题讨论】:
标签: javascript events
自 2011 年年中 Chrome 发布以来,Elements Panel in Google Chrome Developer tools 就拥有此功能,自 2010 年以来 Chrome 开发者频道发布。
此外,为所选节点显示的事件侦听器按照它们在捕获和冒泡阶段被触发的顺序。
Hit command + option + i 在 Mac OSX 和 Ctrl + Shift + i 在 Windows 上在 Chrome 中启动它
【讨论】:
window 对象上的事件,例如message 事件?
使用传统的 element.onclick= handler 或 HTML <element onclick="handler"> 附加的事件处理程序可以通过脚本或调试器中的 element.onclick 属性轻松检索。
目前根本无法从脚本中检索使用 DOM 级别 2 事件 addEventListener 方法和 IE 的 attachEvent 附加的事件处理程序。 DOM Level 3 曾经提出element.eventListenerList 来获取所有的监听器,但是目前还不清楚这是否会成为最终的规范。目前在任何浏览器中都没有实现。
作为浏览器扩展的调试工具可以访问这些类型的侦听器,但我不知道实际上有什么。
一些 JS 框架留下了足够多的事件绑定记录来计算它们一直在做什么。 Visual Event 采用这种方法来发现通过一些流行框架注册的侦听器。
【讨论】:
Chrome 开发工具最近宣布了一些用于 Monitoring JavaScript Events 的新工具。
TL;DR
使用
monitorEvents()收听特定类型的事件。使用
unmonitorEvents()停止收听。使用
getEventListeners()获取 DOM 元素的侦听器。使用 Event Listeners Inspector 面板获取有关事件侦听器的信息。
查找自定义事件
根据我的需要,在 3rd 方代码中发现自定义 JS 事件,getEventListeners() 的以下两个版本非常有用;
getEventListeners(window)getEventListeners(document)如果您知道事件侦听器附加到的 DOM 节点,您将传递它而不是 window 或 document。
已知事件
如果您知道要监控的事件,例如click 在文档正文中您可以使用以下内容:monitorEvents(document.body, 'click');。
您现在应该开始看到控制台中记录了document.body 上的所有点击事件。
【讨论】:
您可以通过查看 DOM 来查看直接附加的事件 (element.onclick = handler)。 您可以使用 FireBug 和 FireQuery 在 Firefox 中查看 jQuery 附加的事件。似乎没有任何方法可以使用 FireBug 查看 addEventListener 添加的事件。但是,您可以使用 Chrome 调试器在 Chrome 中查看它们。
【讨论】:
您可以使用 Allan Jardine 的 Visual Event 来检查页面上几个主要 JavaScript 库中所有当前附加的事件处理程序。它适用于 jQuery、YUI 和其他几个。
Visual Event 是一个 JavaScript 小书签,因此与所有主流浏览器兼容。
【讨论】:
您可以扩展您的 javascript 环境以跟踪事件侦听器。用一些代码包装(或“重载”)本机 addEventListener() 方法,这些代码可以记录从那时起添加的任何事件侦听器。您还必须扩展 HTMLElement.prototype.removeEventListener 以保留准确反映 DOM 中发生的情况的记录。
只是为了说明(未经测试的代码) - 这是一个示例,说明如何“包装” addEventListener 以在对象本身上记录已注册的事件侦听器:
var nativeMethod = HTMLElement.prototype.addEventListener;
HTMLElement.prototype.addEventListener = function (type, listener) {
var el = e.currentTarget;
if(!(el.eventListeners instanceof Array)) { el.eventListeners = []}
el.eventListeners.push({'type':type, 'listener':listener});
nativeMethod.call(el, type, listener);
}
【讨论】:
我很好奇@Rolf 的方法是否真的有效。请记住,这是用相同的包装版本替换标准HTMLElement.prototype.addEventLister() 的“粗略”方式。显然,这只能是一种“用于测试的注入方法”,并且对于任何接近“生产版本”的东西肯定都必须删除。
在测试时我发现,除了一个小故障(他的 e 没有在任何地方定义,但可以很容易地被 this 替换)之外,该方法确实有效,只要作为
addEventListener() 合作仅处理实际元素本身onclick或oninput等属性。我继续研究是否可以使“嗅探”更通用一点,并提出了以下修改版本:
(nativeMethod=>{ // IIFE-closure to manipulate the standard addEventListener method:
HTMLElement.prototype.addEventListener = function (type,fun) {
(this.ELL=this.ELL||[]).push([type,fun]);
nativeMethod.call(this,type,fun);
}
})(HTMLElement.prototype.addEventListener);
// LIST direct and indirect event attachments for element `el`:
function listELfor(el){
const events="click,change,input,keyup,keydown,blur,focus,mouseover,mouseout"
.split(",").map(e=>"on"+e); // possible direct event assignments to check up on
const evlist = (el.ELL||[]).map(([t,f])=>[t,f.toString()]);
events.forEach(e=> el[e] && (evlist[e]=[e.substr(2),el[e].toString()]) )
let p=el.parentNode;
if (p.tagName!=="HTML"){ // if available: run function on parent level recursively:
evlist[p.tagName+(p.id?'#'+p.id:'')+(p.className?'.'+p.className:'')]=listELfor(el.parentNode);
}
return evlist;
};
// ============ TESTING ==========================================
// now, let's do some sample event attachments in different ways:
const sp=document.querySelector('h1 span'); // sp = the target SPAN within H1
sp.addEventListener('click',function(e){console.log('first:',e.target)});
sp.addEventListener('click',function(e){console.log('second:',e.target.tagName)});
sp.addEventListener('click',function(e){console.log('third:',e.target.dataset.val)});
// attach an event to the parent node (H1):
sp.parentNode.addEventListener('click',function(e){console.log('Click event attached to H1, click-target is',e.target.tagName);});
// and finally, let's also assign an onclick event directly by using the ONCLICK attribute:
sp.onclick=e=>console.log('direct onclick on span, text:',e.target.textContent);
// Get all event handler functions linked to `sp`?
const allHandlers=listELfor(sp);
for (id in allHandlers) console.log(id,allHandlers[id]);
h1 span {cursor:pointer}
.as-console-wrapper {max-height:85% !important}
<div id="main-frame-error" class="interstitial-wrapper">
<div id="main-content">
<div class=""></div>
<div id="main-message">
<h1>Hello, <span data-val="123">THESE WORDS ARE CLICKABLE</span></h1>
<p>Some more text here to pad it out. This text should be unresponsive.</p>
</div>
</div>
</div>
IIFE 结构将.addEventListener() 函数处理程序附件作为数组存储在相关DOM 元素的ELL 属性中。函数listELfor(el) 然后获取元素本身的这些函数处理程序,并向上遍历父层次结构以获取对其父级的分配。该函数还将使用onclick 和类似属性处理直接事件分配。
listELfor() 将返回一个具有额外属性的数组对象。这些属性不一定在普通的console.log() 中可见。这就是我使用for (id in allHandlers) 循环的原因。
请注意:
Chrome 将列出这些“额外”的 Array 属性 - 甚至更多的属性,与父级及其父级的事件附件相关,如下所示:
【讨论】: