我正在学习如何创建Chrome扩展。我刚刚开始开发一个用于捕捉YouTube事件的扩展。我想将其与YouTube flash播放器一起使用(稍后我会尝试使其与HTML5兼容)。
清单.json:
{
"name": "MyExtension",
"version": "1.0",
"description": "Gotta catch Youtube events!",
"permissions": ["tabs", "http://*/*"],
"content_scripts" : [{
"matches" : [ "www.youtube.com/*"],
"js" : ["myScript.js"]
}]
}
我的脚本.js:
function state() { console.log("State Changed!"); }
var player = document.getElementById("movie_player");
player.addEventListener("onStateChange", "state");
console.log("Started!");
问题是,当我播放/暂停YouTube视频时,控制台给我 “已启动!",但没有 “状态已更改!"。
当这段代码放进控制台时,它工作了。我做错了什么?
6条答案
按热度按时间tuwxkamq1#
内容脚本在"隔离世界"环境中执行。
使用DOM将代码注入到页面中--该代码将能够 * 访问 * 页面上下文("主世界")的函数/变量,或者 * 向页面上下文公开 * 函数/变量(在您的示例中是
state()
方法)。使用DOM
CustomEvent
行程常式。范例:one、two和three中的一个或多个。chrome
API,请注意:**由于
chrome.*
API不能在页面脚本中使用,您必须在内容脚本中使用它们,并通过DOM消息传递将结果发送到页面脚本(请参阅上面的注解)。网页可能会重新定义或增加/挂钩内置原型,因此如果网页以不兼容的方式执行代码,您的公开代码可能会失败。如果您想确保公开代码在安全的环境中运行,那么您应该a)使用"run_at"声明内容脚本:"document_start"并使用方法2 - 3而不是方法1,或者b)通过空iframe example提取原始原生内置。请注意,对于
document_start
,您可能需要在公开的代码中使用DOMContentLoaded
事件来等待DOM。目录
方法1:插入另一个文件(清单V3/MV2)
当你有很多代码的时候特别好。把代码放在扩展名中的一个文件里,比如
script.js
。然后像这样把它加载到你的content script中:web_accessible_resources
中公开**:否则,控制台中将出现以下错误:
拒绝加载chrome-extension://[ EXTENSIONID ]/script.js。资源必须列在web_accessible_resources清单键中,以便由扩展之外的页面加载。
方法二:注入嵌入式代码(MV2)
当您想要快速运行一小段代码时,此方法非常有用。(另请参阅:How to disable facebook hotkeys with Chrome extension?)的数据。
注意:template literals仅在Chrome 41及更高版本中支持。如果您希望该扩展在Chrome 40-中工作,请使用:
方法2b:使用函数(MV2)
对于一大块代码,引用字符串是不可行的。可以使用函数代替数组,并将其字符串化:
这个方法是可行的,因为字符串和函数上的
+
运算符将所有对象转换为字符串。如果你打算多次使用代码,明智的做法是创建一个函数以避免代码重复。实现可能如下所示:注意:由于函数已序列化,因此原始范围和所有绑定属性都将丢失!
方法三:使用内嵌事件(ManifestV3/MV2)
有时候,你想立即运行一些代码,例如在
<head>
元素创建之前运行一些代码。这可以通过插入一个带有textContent
的<script>
标签来实现(参见方法2/2b)。另一种方法(但不建议使用)是使用内联事件。不建议使用内联事件,因为如果页面定义了禁止内联脚本的内容安全策略,则内联事件侦听器将被阻止。另一方面,由扩展插入的内联脚本仍将运行。如果仍要使用内联事件,请按以下方式操作:
注意:此方法假定没有其他全局事件侦听器处理
reset
事件。如果有,您也可以选择其他全局事件之一。只需打开JavaScript控制台(F12),键入document.documentElement.on
,然后选择可用的事件。方法四:使用chrome.脚本API
world
(仅限清单V3)chrome.scripting.executeScript
与world: 'MAIN'
chrome.scripting.registerContentScripts
与world: 'MAIN'
,也允许runAt: 'document_start'
保证页面脚本的早期执行。与其他方法不同的是,这个方法用于后台脚本或弹出脚本,而不是用于内容脚本。请参阅documentation和examples。
注入代码中的动态值(MV2)
有时候,您需要将任意变量传递给注入的函数。例如:
要插入此代码,您需要将变量作为参数传递给匿名函数。请确保正确实现它!以下将不起作用:
解决方法是在传递参数之前使用
JSON.stringify
。例如:如果您有许多变数,最好使用一次
JSON.stringify
,以改善可读性,如下所示:插入代码中的动态值(ManifestV3)
然后script.js可以读取它:
args
参数,registerContentScripts目前没有(希望将来会添加)。rlcwz9us2#
Rob W出色的回答中唯一缺少的是如何在注入的页面脚本和内容脚本之间进行通信。
在接收端(您的内容脚本或注入的页面脚本)添加事件侦听器:
在发起方(内容脚本或注入的页面脚本)发送事件:
备注:
cloneInto
(一个内置函数)显式地将其克隆到目标中,否则它将失败,并出现安全违规错误。e5nqia273#
我还遇到了加载脚本的顺序问题,这个问题通过顺序加载脚本来解决,加载基于Rob W's answer。
用法示例如下:
事实上,我是一个新来的JS,所以随时为我提供更好的方式。
7gs2gvoe4#
您可以使用我创建的实用程序函数,以便在页面上下文中运行代码并获取返回值。
这是通过将函数序列化为字符串并将其注入到网页来完成的。
该实用程序为available here on GitHub。
用法示例-
sc4hvdpw5#
在内容脚本中,我将script标签添加到绑定了“onmessage”处理程序的head中,在我使用的处理程序中,eval用于执行代码。在展台内容脚本中,我也使用onmessage处理程序,因此我获得了双向通信。Chrome文档
pmListener.js是发布消息URL侦听器
这样,我可以有2路通信之间的CS到真实的的Dom。它非常有用,例如,如果你需要监听webscocket事件,或任何在内存中的变量或事件。
qq24tv8q6#
如果希望注入纯函数而不是文本,可以使用以下方法:
你还可以给函数传递参数(不幸的是,对象和数组都不能被字符串化),把它添加到barethesis中,如下所示: