如何找出 Chrome 扩展程序中附加到特定 HTML 元素的事件侦听器类型?
我在这里发布这个问题,因为我无法将其发布在官方 Chromium 扩展论坛上(或者在它被审核之前有一个很大的延迟)。我必须在 Chromium 扩展中检查是否有特定事件类型的侦听器附加到任意 HTML 元素。在 Firefox 中,我可以使用以下服务来获取此信息:
var listenerService = Components.classes["@mozilla.org/eventlistenerservice;1"]
.getService(Components.interfaces.nsIEventListenerService);
var infos = listenerService.getListenerInfoFor(element, {});
var types = [];
for ( var i = 0; i < infos.length; ++i) {
var info = infos[i].QueryInterface(Components.interfaces.nsIEventListenerInfo);
types.push(info.type);
}
正如我在 Chromium 中看到的,没有类似的 API。因此我尝试过 以下技术(此处建议) ):
我创建了脚本 events_spy.js
:
(function(original) {
Element.prototype.addEventListener = function(type, listener, useCapture) {
if (typeof (this._handlerTypes) == 'undefined') {
this._handlerTypes = {};
}
this._handlerTypes[type] = true;
return original.apply(this, arguments);
}
})(Element.prototype.addEventListener);
(function(original) {
Element.prototype.removeEventListener = function(type, listener,useCapture) {
if (typeof (this._handlerTypes) != 'undefined') {
delete this._handlerTypes[type];
}
return original.apply(this, arguments);
}
})(Element.prototype.removeEventListener);
我在 manifest.json
中声明此脚本,如下所示:
"content_scripts" : [{
"matches" : [ "http://*/*", "https://*/*" ],
"js" : [ "content/events_spy.js" ],
"run_at" : "document_start",
"all_frames" : true
},
...
]
然后,我在以下 HTML 页面:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<a id="test" href="#">Click here</a>
<script type="application/javascript">
document.getElementById("test").addEventListener("click", function()
{ alert("clicked"); }, false);
</script>
</body>
</html>
不幸的是,这不起作用 - 我看不到调试器停止
在我的自定义 addEventListener()
函数内部。我做错了什么?
谢谢!
编辑:最终(脏)解决方案,感谢@kdzwinel
var injectedJS = "\
(function(original) { \
Element.prototype.addEventListener = function(type, listener, useCapture) { \
var attr = this.getAttribute('_handlerTypes'); \
var types = attr ? attr.split(',') : []; \
var found = false; \
for (var i = 0; i < types.length; ++i) { \
if (types[i] == type) { \
found = true; \
break; \
} \
} \
if (!found) { \
types.push(type); \
} \
this.setAttribute('_handlerTypes', types.join(',')); \
return original.apply(this, arguments); \
} \
})(Element.prototype.addEventListener); \
\
(function(original) { \
Element.prototype.removeEventListener = function(type, listener, useCapture) { \
var attr = this.getAttribute('_handlerTypes'); \
var types = attr ? attr.split(',') : []; \
var removed = false; \
for (var i = 0; i < types.length; ++i) { \
if (types[i] == type) { \
types.splice(i, 1); \
removed = true; \
break; \
} \
} \
if (removed) { \
this.setAttribute('_handlerTypes', types.join(',')); \
} \
return original.apply(this, arguments); \
} \
})(Element.prototype.removeEventListener); \
";
var script = document.createElement("script");
script.type = "text/javascript";
script.appendChild(document.createTextNode(injectedJS));
document.documentElement.appendChild(script);
每个具有附加事件侦听器的 HTML 元素都将具有一个特殊属性“_handlerTypes”,其中包含逗号分隔的事件列表。并且可以从 Chrome 扩展程序的内容脚本访问此属性!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
当我在单个独立的 HTML 文件上测试您的脚本时,它运行良好。但由于以下政策,它不能作为 Chrome 扩展程序运行:
为了安全和避免冲突,所有内容都被沙箱化。页面和内容脚本之间的所有通信都必须通过 DOM 处理。
看来有人遇到了与您相同的问题并使其工作:
如果您设法使用
events_spy.js
而不是$(node).data("events")
,您的扩展程序可能会工作得更好。此外,他在页面和内容脚本之间进行通信的方式也很丑陋。使用文档(“与嵌入页面通信”部分)中描述的解决方案。
Your script is working fine when I test it on a single, standalone HTML file. It is not working as a Chrome extension though because of this policy:
Everything is sandboxed for security and to avoid conflicts. All communication between page and content script must be handled via DOM.
It looks like someone had the same problem as you do and made it work:
Your extension may work even better if you manage to use your
events_spy.js
instead of$(node).data("events")
.Also the way he communicates between page and content script is ugly. Use solution described in the docs (section 'Communication with the embedding page').
Chrome 控制台现在支持
getEventListeners()
,请参阅 https://code.google.com/p/accessibility-developer-tools/source/browse/src/audits/UnfocusableElementsWithOnClick.js举个例子。也就是说,我当前的用例是让上面链接的脚本在 Firefox 文档而不是 Chrome 扩展中工作,这意味着我必须添加自己的事件收集技巧。我仍在寻找一种简单、体面的方法。如果我找到一个,我将发布上述脚本的另一个补丁。 (此时,我正在考虑创建一个内置 Firefox 支持的分支,以及更多一些用于解析审核结果的辅助方法。)
Chrome's console now supports
getEventListeners()
, see https://code.google.com/p/accessibility-developer-tools/source/browse/src/audits/UnfocusableElementsWithOnClick.js for an example.That said, my current use-case is getting the above-linked script to work in a Firefox document rather than a Chrome extension, which means I'll have to add my own event-gathering hacks. I'm still hunting for a simple, decent approach. If I find one, I'll post another patch to the above script. (At this point I'm thinking of making a fork with Firefox support built-in and a few more helper methods for parsing the audit results.)
花了很多时间让它工作,所以发布我的解决方案(没有 jQuery)...
首先,我们需要在第一次调用页面的 javascript 之前注入 addEventListener 和 removeEventListener 的“修补”版本。我们只能在 DOM 内容加载之后执行此操作,但在解析之前我们需要它。
content_script.js:
EventTarget 的修补版本将订阅的事件侦听器存储在
element.__eventListeners
对象内,该对象无法从content_script.js
访问。但需要获得正确数量的事件。现在订阅事件的每个对象都将具有__eventname
属性(例如:,值表示订阅的事件侦听器的数量)。
eventtarget.js:
在我的解决方案中,我使用单独的文件
eventtarget.js
,它需要包含在清单文件的web_accessable_resources
部分中。另请注意,run_at
必须设置为document_start
才能订阅DOMContentLoaded
事件。不需要额外的权限。manifest.json:
其工作原理的小例子:
Spent much time to get it work, so publishing my solution (without jQuery)...
First of all, we need to inject "patched" version of addEventListener and removeEventListener before the first call of page's javascript. We can do it only after DOM content is loaded, but we need it before it's parsed.
content_script.js:
Patched version of EventTarget stores subscribed event listeners inside the
element.__eventListeners
object, which is inaccessable fromcontent_script.js
. But it's needed to get the right number of events. Each object subscribed on event now will have__eventname
attribute (for example:<div id="test" __click="1" __mouseover="2">
, the value indicates count of event listeners subscribed).eventtarget.js:
In my solution i use separate file
eventtarget.js
, it's needed to be included in theweb_accessable_resources
section of manifest file. Also note,run_at
must be set todocument_start
to subscribe toDOMContentLoaded
event. No additional permissions is needed.manifest.json:
Little example of how it works: