为什么chrome.tabs.executeScript()需要更改当前的网站DOM?如何使用jQuery来实现相同的效果?

更新:我最近收到了很多否定信息,这是一个非常古老的问题(2。5年)。 我甚至不再使用jquery了。 所以请rest一下!


我之前已经制作了一些chrome扩展程序,但这是我第一次需要在网站上更改用户当前正在查看的内容以响应扩展弹出窗口中的按钮点击。

正如我意识到只是执行一些jQuery行无效。 什么有效是我在谷歌开发者页面示例扩展中找到的方法: chrome.tabs.executeScript()但我不知道为什么它是必要的。

必须有一些我不知道的基本概念。 有人能解释一下吗? 我可以执行jQuery(加载)吗?

完整示例:

 $('#change_button').on('click', function() { //this doesen't work $("body").css({backgroundColor: 'blue'}); //but this line does chrome.tabs.executeScript(null, {code:"document.body.style.backgroundColor='red'"}); }); 

实际上我需要jQuery严重在DOM中做一些更改并回应它们,即:

 if( $(".list,.list-header-name").hasClass("tch")) { $(".list,.list-header-name").addClass("tch"); } 

您在Chrome扩展程序中运行的Javascript可以在后台页面或某个弹出页面中运行。 它不是浏览器中运行扩展程序时的页面。 这就是您需要在特定选项卡中执行脚本的原因。

要更好地将其可视化,请右键单击扩展的按钮,然后选择“检查弹出窗口”。 对于后台页面也是如此,转到你的(推测)解压扩展的chrome://扩展并点击后台页面,你就可以使用开发人员工具来查看发生了什么。

为了在chrome扩展中打包某些资源,以便以后可以通过网页直接访问它们,您可以使用Web Accessible Resources 。 基本上,您声明了网页可以访问的文件,然后您可以使用“chrome-extension:// [PACKAGE ID] / [PATH]”forms的URL加载或注入它们。 为了使用这种机制并使打包的jQuery库可用于网页,我们添加:

 "web_accessible_resources": [ "jquery-2.2.2.min.js" ] 

在manifest.json中。

要从扩展程序访问网页,您需要为其添加权限 :

 "permissions" : [ "tabs", [...] "", "file://*/*", "http://*/*", "https://*/*" ], 

也在manifest.json中。 是一个特殊的关键字,不是我在那里添加的,它是任何类型的URL的占位符,无论架构如何。 如果您只想要http和httpsurl,请仅使用最后两个。

这是一个函数,它在某个选项卡的页面中加载jQuery,为其指定名称ex $,以便它不与其他库或jQuery版本冲突。

 function initJqueryOnWebPage(tab, callback) { // see if already injected chrome.tabs.executeScript(tab.id,{ code: '!!window.ex$'},function(installed) { // then return if (installed[0]) return; // load script from extension (the url is chrome.extension.getUrl('jquery-2.2.2.min.js') ) chrome.tabs.executeScript(tab.id,{ file: 'jquery-2.2.2.min.js' },function() { // make sure we get no conflicts // and return true to the callback chrome.tabs.executeScript(tab.id,{ code: 'window.ex$=jQuery.noConflict(true);true'},callback); }); }); } 

此函数将jQuery从扩展加载到网页,然后在安装时执行回调。 从那时起,可以使用相同的chrome.tabs.executeScript(tab.id,{ code/file:在远程页面上使用jQuery运行脚本。

由于它使用带有file选项的executeScript方法,因此它不需要脚本的web_accessible_resources声明

从下面的一个很好的评论中,我了解了执行环境 。 executeScript在“孤立的世界”中运行,因此它无法访问全局变量,并且执行的任何操作都不会创建可由网页访问的内容。 所以上面所有复杂的function都是矫枉过正的。 如果您不想与页面上的代码交互,但只是与其DOM交互,并且不关心代码中的任何冲突,您仍然可以使用此系统

但扩展程序可以访问选项卡的文档。 这是一个函数,它使用首先描述的web_accessible_resources机制在网页中创建一个脚本标记,然后加载包装在扩展中的jQuery库:

 // executeScript's a function rather than a string function remex(tabId, func,callback) { chrome.tabs.executeScript(tabId,{ code: '('+func.toString()+')()' },callback); } function initJqueryOnWebPage(tab, callback) { // the function to be executed on the loaded web page function f() { // return if jQuery is already loaded as ex$ if (window.ex$) return; // create a script element to load jQuery var script=document.createElement('script'); // create a second script tag after loading jQuery, to avoid conflicts script.onload=function() { var noc=document.createElement('script'); // this should display in the web page's console noc.innerHTML='window.ex$=jQuery.noConflict(true); console.log(\'jQuery is available as \',window.ex$);'; document.body.appendChild(noc); }; // use extension.getURL to get at the packed script script.src=chrome.extension.getURL('jquery-2.2.2.min.js'); document.body.appendChild(script); }; // execute the content of the function f remex(tab.id,f,function() { console.log('jQuery injected'); // this should log in the background/popup page console if (typeof(callback)=='function') callback(); }); } 

将脚本发送到executeScripts很麻烦,这就是我使用上面的remex函数的原因。 请注意,在“隔离世界”环境中执行函数的remex与将脚本标记附加到文档时由选项卡执行的代码之间存在差异。 但是,在这两种情况下,实际执行的是字符串,因此请注意不要尝试跨上下文传递对象或函数。

两个连续的remex调用将在它们自己的上下文中执行,因此即使你将jQuery加载到一个中,你也不会在下一个中使用它。 但是,可以使用与initJqueryOnWebPage中相同的系统,这意味着将代码添加到页面上的脚本中。 这里有一些代码,就像remex一样使用它:

 function windowContextRemex(tabId,func,callback) { var code=JSON.stringify(func.toString()); var code='var script = document.createElement("script");'+ 'script.innerHTML = "('+code.substr(1,code.length-2)+')();";'+ 'document.body.appendChild(script)'; chrome.tabs.executeScript(tabId, { code : code }, function () { if (callback) return callback.apply(this, arguments); }); } 

所以我在这里certificate:

  • 在隔离的上下文中执行代码,该上下文只能访问在选项卡中打开的页面的DOM
  • 在与上面相同的上下文中执行打包的脚本
  • 注入在页面上下文中运行的脚本

缺什么:

  • 链接代码的好方法,这样您就不必手动创建脚本元素
  • 通过insertCSS加载CSS文件(类似于带有文件选项的executeScript,但直接在页面中加载)

弹出页面位于扩展的上下文中,而当前网页中的DOM是另一个上下文,它们是不同的进程,因此它们需要一些通信方式, tabs.executeScript是一种以编程方式将代码插入页面的方法。

如果你非常依赖jQuery,而不是直接插入代码,你可以先注入文件,然后随意使用jquery方法。

 chrome.tabs.executeScript(null, {file: "jquery.js"}, function() { chrome.tabs.executeScript(null, {code:"$("body").css({backgroundColor: 'blue'})"); 

除了编程注入,您还可以通过在manifest.json指定matches字段来注入内容脚本。 这样,当用户单击弹出页面中的按钮时,您可以使用消息传递在扩展进程和内容脚本之间进行通信,然后使用内容脚本来操作当前页面。

你缺少的是弹出窗口的javascript只影响弹出窗口的DOM。 要影响选项卡的DOM,您需要使用内容脚本(来自清单,或通过注入它)。

我们假设您将此作为popup.html:

    popup html    

这就是popup.js:

 $('#onlyPopup').click(function() { $("body").css('backgroundColor','blue'); }); $("#affectWebpage").click(function() { chrome.tabs.executeScript(null, {code:"document.body.style.backgroundColor='red'"}); }); $("#jqWebpage").click(function() { chrome.tabs.executeScript(null, {file:"jquery.min.js"}, callback); }); function callback() { chrome.tabs.executeScript(null, {code:"$('body').css('backgroundColor','yellow');"}); } 

因此,您原来的“无法正常工作”的代码确实可行,而不是您期望的方式。 它为弹出窗口的背景着色,因为这是代码执行的地方。 要在选项卡中执行jQuery代码,您需要首先注入jQuery然后运行您的代码(并且需要的唯一权限是activeTab )。 诱惑会是

 $("#jqWebpage").click(function() { // don't do this chrome.tabs.executeScript(null, {file:"jquery.min.js"}); chrome.tabs.executeScript(null, {code:"$('body').css('backgroundColor','yellow');"}); }); 

但由于异步性,这不起作用。 这两行将在文件完全注入之前运行。 这意味着第二行代码可以(并且可能会)在jQuery完全加载之前运行,结果不明。 相反,您需要使用callback参数在jQuery加载完成后执行所需的脚本。