如何在 Google Chrome 的 Greasemonkey 脚本中使用 jQuery?

Posted

技术标签:

【中文标题】如何在 Google Chrome 的 Greasemonkey 脚本中使用 jQuery?【英文标题】:How can I use jQuery in Greasemonkey scripts in Google Chrome? 【发布时间】:2011-01-15 20:33:23 【问题描述】:

你们中的一些人可能知道,谷歌浏览器对 Greasemonkey 脚本设置了一些严格的限制。

Chromium 不支持 @require@resourceunsafeWindowGM_registerMenuCommandGM_setValueGM_getValue

如果没有要求,我找不到将 jQuery 库包含在 Google Chrome 下的 Greasemonkey 脚本中的方法。

有人对这件事有什么建议吗?

【问题讨论】:

值得注意的是,带有 Tampermonkey 的 Google Chrome 现在支持 @require,这比答案中的方法简单得多。 Tampermonkey 还支持 unsafeWindow,这对于已经有 jQuery 的页面非常有用。 var $ = unsafeWindow.jQuery; @require 在您不担心与加载时绑定到 $ 的数千或数百万其他 JS 库中的任何一个发生冲突的网站上效果很好。但是,如果您正在使用 $ 为其他东西编写一个站点的脚本,或者更糟糕的是编写一个在每个站点上运行的脚本,请使用下面 tghw 解释的相对安全的加载机制。跨度> 【参考方案1】:

来自"User Script Tip: Using jQuery - Erik Vold's Blog"

// ==UserScript==
// @name         jQuery For Chrome (A Cross Browser Example)
// @namespace    jQueryForChromeExample
// @include      *
// @author       Erik Vergobbi Vold & Tyler G. Hicks-Wright
// @description  This userscript is meant to be an example on how to use jQuery in a userscript on Google Chrome.
// ==/UserScript==

// a function that loads jQuery and calls a callback function when jQuery has finished loading
function addJQuery(callback) 
  var script = document.createElement("script");
  script.setAttribute("src", "//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js");
  script.addEventListener('load', function() 
    var script = document.createElement("script");
    script.textContent = "window.jQ=jQuery.noConflict(true);(" + callback.toString() + ")();";
    document.body.appendChild(script);
  , false);
  document.body.appendChild(script);


// the guts of this userscript
function main() 
  // Note, jQ replaces $ to avoid conflicts.
  alert("There are " + jQ('a').length + " links on this page.");


// load jQuery and execute the main function
addJQuery(main);

【讨论】:

而不是 addEventListener 中的 3 行用于“加载”,而不是“回调();”只是工作? 我的页面已经包含了jQuery,但是在userscript中使用jQuery似乎还是需要上面的代码。但是,两个 jQuery 包含可能会导致冲突,因此 main() 函数的第一行可能需要 jQuery.noConflict(); 我在自己的用户脚本模板中将script.textContent = "(" + callback.toString() + ")();"; 行修改为script.textContent = "jQuery.noConflict();(" + callback.toString() + ")();";,这样就不会出现任何意外的冲突。 :) @hippietrail 在main(),您可以使用$.loadScript(),然后在loadScript 完成加载jQueryUI 后运行其他所有内容。 -1:使用该方法,main 中的代码将在目标页面的上下文中执行,这意味着(除其他外)目标页面的跨站点请求策略适用 - (例如,我不得不重新注入GM_xmlhttpRequest,然后才发现它对我没有帮助)。最后我只是简单的复制粘贴了jquery.min.js的代码。【参考方案2】:

我已经基于Erik Vold's script 编写了一些函数来帮助我在文档中运行函数、代码和其他脚本。您可以使用它们将 jQuery 加载到页面中,然后在全局 window 范围内运行代码。

示例用法

// ==UserScript==
// @name           Example from http://***.com/q/6834930
// @version        1.3
// @namespace      http://***.com/q/6834930
// @description    An example, adding a border to a post on Stack Overflow.
// @include        http://***.com/questions/2246901/*
// ==/UserScript==

var load,execute,loadAndExecute;load=function(a,b,c)var d;d=document.createElement("script"),d.setAttribute("src",a),b!=null&&d.addEventListener("load",b),c!=null&&d.addEventListener("error",c),document.body.appendChild(d);return d,execute=function(a)var b,c;typeof a=="function"?b="("+a+")();":b=a,c=document.createElement("script"),c.textContent=b,document.body.appendChild(c);return c,loadAndExecute=function(a,b)return load(a,function()return execute(b));

loadAndExecute("//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js", function() 
    $("#answer-6834930").css("border", ".5em solid black");
);

您可以click here 安装它,如果您相信我没有试图欺骗您安装恶意软件并且没有人编辑我的帖子以指向其他内容。重新加载页面,您应该会在我的帖子周围看到一个边框。

功能

load(url, onLoad, onError)

url 处的脚本加载到文档中。可选地,可以为onLoadonError 提供回调。

execute(functionOrCode)

在文档中插入一个函数或代码字符串并执行它。这些函数在插入之前会转换为源代码,因此它们会丢失当前的作用域/闭包,并在全局 window 作用域下运行。

loadAndExecute(url, functionOrCode)

快捷方式;这会从url 加载一个脚本,如果成功则插入并执行functionOrCode

代码

function load(url, onLoad, onError) 
    e = document.createElement("script");
    e.setAttribute("src", url);

    if (onLoad != null)  e.addEventListener("load", onLoad); 
    if (onError != null)  e.addEventListener("error", onError); 

    document.body.appendChild(e);

    return e;


function execute(functionOrCode) 
    if (typeof functionOrCode === "function") 
        code = "(" + functionOrCode + ")();";
     else 
        code = functionOrCode;
    

    e = document.createElement("script");
    e.textContent = code;

    document.body.appendChild(e);

    return e;


function loadAndExecute(url, functionOrCode) 
    load(url, function()  execute(functionOrCode); );

【讨论】:

@cyphunk 是的,保存这几个字符对我来说至关重要。实际上,我对离开这篇文章如此毫无意义地使用它感到很愚蠢。我会删除它。【参考方案3】:

通过调用jQuery.noConflict(true) 使用jQuery 不用担心冲突。像这样:

function GM_main ($) 
    alert ('jQuery is installed with no conflicts! The version is: ' + $.fn.jquery);


add_jQuery (GM_main, "1.7.2");

function add_jQuery (callbackFn, jqVersion) 
    jqVersion       = jqVersion || "1.7.2";
    var D           = document;
    var targ        = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
    var scriptNode  = D.createElement ('script');
    scriptNode.src  = 'http://ajax.googleapis.com/ajax/libs/jquery/'
                    + jqVersion
                    + '/jquery.min.js'
                    ;
    scriptNode.addEventListener ("load", function () 
        var scriptNode          = D.createElement ("script");
        scriptNode.textContent  =
            'var gm_jQuery  = jQuery.noConflict (true);\n'
            + '(' + callbackFn.toString () + ')(gm_jQuery);'
        ;
        targ.appendChild (scriptNode);
    , false);
    targ.appendChild (scriptNode);



但是,对于跨浏览器脚本,为什么不在可能的情况下利用一个好的、快速的本地 jQuery 副本?

以下作为 Chrome 用户脚本和 Greasemonkey 脚本工作,如果平台支持,它使用漂亮的本地 @require jQuery 副本。

// ==UserScript==
// @name     _Smart, cross-browser jquery-using script
// @include  http://YOUR_SERVER.COM/YOUR_PATH/*
// @require  http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @grant    GM_info
// ==/UserScript==

function GM_main ($) 
    alert ('jQuery is installed with no conflicts! The version is: ' + $.fn.jquery);


if (typeof jQuery === "function") 
    console.log ("Running with local copy of jQuery!");
    GM_main (jQuery);

else 
    console.log ("fetching jQuery from some 3rd-party server.");
    add_jQuery (GM_main, "1.7.2");


function add_jQuery (callbackFn, jqVersion) 
    var jqVersion   = jqVersion || "1.7.2";
    var D           = document;
    var targ        = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
    var scriptNode  = D.createElement ('script');
    scriptNode.src  = 'http://ajax.googleapis.com/ajax/libs/jquery/'
                    + jqVersion
                    + '/jquery.min.js'
                    ;
    scriptNode.addEventListener ("load", function () 
        var scriptNode          = D.createElement ("script");
        scriptNode.textContent  =
            'var gm_jQuery  = jQuery.noConflict (true);\n'
            + '(' + callbackFn.toString () + ')(gm_jQuery);'
        ;
        targ.appendChild (scriptNode);
    , false);
    targ.appendChild (scriptNode);

【讨论】:

+1 令人印象深刻,在所有这些答案中,你的答案是唯一一个说要使用 @require,如果页面有 jQuery,你也可以使用 $ = unsafeWindow.jQuery(我只在 Tampermonkey 中测试过)。 警告:某些版本的 IE 没有 console.log() 除非你安装了开发工具,所以脚本会崩溃。通常,只有在将脚本发布到开发人员和 QA 人员之外,您才会发现这一点。 @Parsingphase,IE 在这里几乎不适用。最后我检查了一下,IE 仍然不能很好地支持用户脚本(¿?)。 IE 10 改变了这种情况吗? Brock - 好点子,我没能将 IE 的不兼容矩阵牢记在心。因此,它并不直接适用于用户脚本(尽管人们似乎偶尔会尝试实现 IE 解决方案),但更多的是一般的问题。【参考方案4】:

如果页面已经有 jQuery,那么就按照这个模板:

// ==UserScript==
// @name          My Script
// @namespace     my-script
// @description   Blah
// @version       1.0
// @include       http://site.com/*
// @author        Me
// ==/UserScript==

var main = function () 

    // use $ or jQuery here, however the page is using it

;

// Inject our main script
var script = document.createElement('script');
script.type = "text/javascript";
script.textContent = '(' + main.toString() + ')();';
document.body.appendChild(script);

【讨论】:

我认为这不起作用,因为用户脚本无权访问文档窗口? @Christoph 它确实有效,我有而且我仍在使用 userscript 使用这种方法。 这实际上是将greasemonkey脚本注入到页面中。所以可能绕过了greasemonkey的一些保护措施。 @Thymine 我注意到这种方法确实将用户脚本注入到不需要的页面中。我不得不将注入部分包装在检查window.locationif 语句中。【参考方案5】:

简单的方法是使用required关键字:

// @require     http://code.jquery.com/jquery-latest.js

【讨论】:

只有扩展实现支持。 @user2284570 我可以为任何浏览器找到的每个用户脚本扩展都支持它。 @MattM。我的意思是你不必在 Opera 和 Chrome 上安装扩展来运行用户脚本。【参考方案6】:

当这些脚本实际上不使用任何特权功能时(GM_* 函数等)... p>

只需将脚本本身插入页面 DOM 并执行!最好的部分是这种技术在 Firefox+Greasemonkey 上同样有效,因此您可以对两者使用相同的脚本:

var script = document.createElement("script");
script.type = "text/javascript";
script.textContent = "(" + threadComments.toString() + ")(jQuery)";
document.body.appendChild(script);

function threadComments($) 
    // taken from kip's http://userscripts-mirror.org/scripts/review/62163
    var goodletters = Array('\u00c0','\u00c1','\u00c2','\u00c3','\u00c4','\u00c5','\u00c6','\u00c7'
                             ,'\u00c8','\u00c9','\u00ca','\u00cb','\u00cc','\u00cd','\u00ce','\u00cf'
                                      ,'\u00d1','\u00d2','\u00d3','\u00d4','\u00d5','\u00d6'         
                             ,'\u00d8','\u00d9','\u00da','\u00db','\u00dc','\u00dd'                  
                             ,'\u00e0','\u00e1','\u00e2','\u00e3','\u00e4','\u00e5','\u00e6','\u00e7'
                             ,'\u00e8','\u00e9','\u00ea','\u00eb','\u00ec','\u00ed','\u00ee','\u00ef'
                                      ,'\u00f1','\u00f2','\u00f3','\u00f4','\u00f5','\u00f6'         
                             ,'\u00f8','\u00f9','\u00fa','\u00fb','\u00fc','\u00fd'         ,'\u00ff').join('');

    // from Benjamin Dumke's http://userscripts-mirror.org/scripts/review/68252
    function goodify(s)
      
         good = new RegExp("^[" + goodletters + "\\w]3");
         bad = new RegExp("[^" + goodletters + "\\w]");
         original = s;
         while (s.length >3 && !s.match(good)) 
            s = s.replace(bad, "");
            
         if (!s.match(good))
         
           // failed, so we might as well use the original
           s = original;
         
         return s;
        

    in_reply_to = ;


    function who(c, other_way) 


        if (other_way)
        
            // this is closer to the real @-reply heuristics
            m = /@(\S+)/.exec(c);
        
        else
        
            m = /@([^ .:!?,()[\]]+)/.exec(c);
        
        if (!m) return
        if (other_way) return goodify(m[1]).toLowerCase().slice(0,3);
        else return m[1].toLowerCase().slice(0,3);
    

    function matcher(user, other_way) 
        if (other_way)
        
            return function () 
                return goodify($(this).find(".comment-user").text()).toLowerCase().slice(0,3) == user
                
        
        else
        
            return function () 
                return $(this).find(".comment-user").text().toLowerCase().slice(0,3) == user
                
        
    

    function replyfilter(id) 
        return function() 
            return in_reply_to[$(this).attr("id")] == id;
        
    

    function find_reference() 
        comment_text = $(this).find(".comment-text").text();
        if (who(comment_text))
        
            fil = matcher(who(comment_text));
            all = $(this).prevAll("tr.comment").filter(fil);
            if (all.length == 0)
            
                // no name matched, let's try harder
                fil = matcher(who(comment_text, true), true);
                all = $(this).prevAll("tr.comment").filter(fil);
                if (all.length == 0) return
            
            reference_id = all.eq(0).attr("id");
            in_reply_to[$(this).attr("id")] = reference_id;
        
    


    // How far may comments be indented?
    // Note that MAX_NESTING = 3 means there are
    // up to *four* levels (including top-level)
    MAX_NESTING = 3

    // How many pixels of indentation per level?
    INDENT = 30

    function indenter(parent) 

        for (var i = MAX_NESTING; i > 0; i--)
        
            if (parent.hasClass("threading-" + (i-1)) || (i == MAX_NESTING && parent.hasClass("threading-" + i)))
            
                return function() 
                    $(this).addClass("threading-" + i).find(".comment-text").css("padding-left": INDENT*i);
                
            
        

        return function() 
            $(this).addClass("threading-1").find(".comment-text").css("padding-left": INDENT);
        

    

    function do_threading()
        id = $(this).attr("id");
        replies = $(this).nextAll("tr.comment").filter(replyfilter(id));
        ind = indenter($(this));
        replies.each(ind);
        replies.insertAfter(this);
    

    function go() 
        $("tr.comment").each(find_reference);
        $("tr.comment").each(do_threading);
    

    $.ajaxSetup(complete: go);
    go();

(在 meta.*** 上从 Shog9 毫无歉意地被盗,因为他没有把它移到这里,我必须删除 meta 帖子..)

【讨论】:

【参考方案7】:

此外,您可以使用 jQuery 将脚本打包到 Chrome 扩展程序。见Google Chrome's Content Scripts。

与 Greasemonkey 脚本不同,Chrome 扩展程序可以自动更新。

【讨论】:

是的,这会更容易。但我现在真的更喜欢通过 userscripts.org 维护我的脚本,而不是使用 google 扩展存储库创建冗余。 上传到 Google Web Store 需要 5 美元。【参考方案8】:

更简单的解决方案:将 jquery.min.js 的内容剪切并粘贴到用户脚本的顶部。完毕。

我发现推荐答案存在各种问题。 addJQuery() 解决方案适用于大多数页面,但在许多页面上都有错误。如果您遇到问题,只需将 jquery 内容复制并粘贴到您的脚本中即可。

【讨论】:

是的,我认为这是最有意义的,因为您甚至可以编写一个小的构建脚本,通过完全按照您在此处的建议来生成 chrome 版本。【参考方案9】:

我想知道你是否不能在你的 GM 脚本中依赖 document.defaultView.jQuery ala:

if (document.defaultView.jQuery) 
  jQueryLoaded(document.defaultView.jQuery);
 else 
  var jq = document.createElement('script');
  jq.src = 'http://jquery.com/src/jquery-latest.js';
  jq.type = 'text/javascript';
  document.getElementsByTagName('head')[0].appendChild(jq);
  (function()  
    if (document.defaultView.jQuery) jQueryLoaded(document.defaultView.jQuery);
    else setTimeout(arguments.callee, 100);
  )();


function jQueryLoaded($) 
  console.dir($);

【讨论】:

【参考方案10】:

另一种方法是修改您的脚本以手动加载 jQuery。来自http://joanpiedra.com/jquery/greasemonkey/的示例:

// Add jQuery
var GM_JQ = document.createElement('script');
GM_JQ.src = 'http://jquery.com/src/jquery-latest.js';
GM_JQ.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(GM_JQ);

// Check if jQuery's loaded
function GM_wait() 
    if(typeof unsafeWindow.jQuery == 'undefined')  window.setTimeout(GM_wait,100); 
else  $ = unsafeWindow.jQuery; letsJQuery(); 

GM_wait();

// All your GM code must be inside this function
function letsJQuery() 
    alert($); // check if the dollar (jquery) function works

编辑:DRATS!经过测试,此代码似乎不起作用,因为 Google Chrome 在与实际网页不同的范围/进程中运行用户脚本/扩展。您可以使用 XmlhttpRequest 下载 jQuery 代码,然后对其进行评估,但您必须将代码托管在允许使用 Cross-Origin Resource Sharing 的服务器上,使用 Access-Control-Allow-Origin: * 标头。可悲的是 NONE of the current CDNs 带有 jQ​​uery 支持。

【讨论】:

【参考方案11】:

将 jQuery 嵌入 Chrome 控制台的完美扩展,就像您想象的那样简单。如果 jQuery 已经嵌入到页面中,这个扩展也会提示。

此扩展用于将 jQuery 嵌入到您想要的任何页面中。它允许在控制台shell中使用jQuery(您可以通过“Ctrl+Shift+j”调用Chrome控制台)。

要将 jQuery 嵌入到选定的选项卡中,请单击扩展按钮。

分机链接:https://chrome.google.com/extensions/detail/gbmifchmngifmadobkcpijhhldeeelkc

【讨论】:

这真的不是答案。如果不需要,我为什么要加载 jQuery?

以上是关于如何在 Google Chrome 的 Greasemonkey 脚本中使用 jQuery?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Chrome 应用程序的 Google Chrome 中隐藏地址栏?

如何解决ajax在google chrome浏览器上失效

如何安装低版本google chrome

如何在 python 中使用现有的 google chrome 配置文件和 selenium chrome webdriver?

如何在Chrome上可以登录Google

如何在Chrome上可以登录Google