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

Hex*_*dus 7 jquery google-chrome-extension

更新:我最近收到了很多负面消息,认为这是一个非常古老的问题(2.5年).我甚至不再使用jquery了.所以请休息一下!


我之前已经制作了一些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'"});

});
Run Code Online (Sandbox Code Playgroud)

实际上我非常需要jQuery在DOM中做一些更改并对它们做出响应,即:

if( $(".list,.list-header-name").hasClass("tch"))
{
  $(".list,.list-header-name").addClass("tch");
}
Run Code Online (Sandbox Code Playgroud)

Sid*_*dex 2

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

为了更好地可视化这一点,右键单击扩展程序的按钮,然后选择检查弹出窗口。对于后台页面也是如此,转到 chrome://extensions 到您的(可能是)解压的扩展程序,然后单击后台页面,您就可以使用开发人员工具来查看发生了什么。

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

"web_accessible_resources": [
    "jquery-2.2.2.min.js"
]
Run Code Online (Sandbox Code Playgroud)

在manifest.json中。

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

"permissions" : [
    "tabs",
    [...]
    "<all_urls>",
    "file://*/*",
    "http://*/*",
    "https://*/*"
],
Run Code Online (Sandbox Code Playgroud)

也在manifest.json中。<all_urls>是一个特殊关键字,不是我在其中添加的,它是任何类型 URL 的占位符,无论架构如何。如果您只需要 http 和 https URL,则仅使用最后两个。

这是一个在某个选项卡的页面中加载 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);
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

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

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

从下面的一个很好的评论中,我了解了执行环境。executeScript 在“孤立的世界”中运行,因此它无法访问全局变量,并且执行的任何内容都不会创建可由网页访问的内容。所以上面整个复杂的功能是多余的。如果您不想与页面上的代码交互,而只想与 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();
    });
}
Run Code Online (Sandbox Code Playgroud)

发送脚本到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);
    });
}
Run Code Online (Sandbox Code Playgroud)

所以我在这里演示了:

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

缺什么:

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