Comment puis-je utiliser jQuery dans les scripts Greasemonkey dans Google Chrome?

157

Comme certains d'entre vous le savent peut-être, Google Chrome a mis de sévères limitations sur les scripts Greasemonkey.

Chrome ne supporte pas @require, @resource, unsafeWindow, GM_registerMenuCommand, GM_setValueou GM_getValue.

Sans exiger, je ne trouve pas de moyen d'inclure la bibliothèque jQuery dans le script Greasemonkey sous Google Chrome.

Quelqu'un a-t-il des conseils à ce sujet?

Alekc
la source
19
Il convient de noter que Google Chrome avec Tampermonkey est pris en charge pour le @requiremoment, ce qui est une approche beaucoup plus simple que celles des réponses.
Steen Schütt
2
Tampermonkey prend également en charge unsafeWindow, ce qui est très pratique pour les pages qui ont déjà jQuery. var $ = unsafeWindow.jQuery;
Tim Goodman
1
@requirefonctionne très bien sur les sites où vous ne craignez pas d'entrer en conflit avec l'un des milliers ou des millions d'autres bibliothèques JS qui se lient à $ lors du chargement. Cependant, si vous écrivez un script pour un site en utilisant $ pour autre chose, ou pire encore, écrivez un script à exécuter sur tous les sites, utilisez le mécanisme de chargement relativement sûr expliqué par tghw ci-dessous.
GDorn

Réponses:

192

Tiré de "Astuce de script utilisateur: Utilisation de jQuery - Blog d'Erik Vold"

// ==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);
tghw
la source
Au lieu des 3 lignes dans addEventListener pour 'load', ne serait pas "callback ();" juste travailler?
crdx
6
Ma page inclut déjà jQuery, mais il semble que le code ci-dessus soit toujours nécessaire pour utiliser jQuery dans le usercript. Cependant, deux inclusions jQuery peuvent provoquer un conflit, donc la première ligne de votre fonction main () peut avoir besoin d'être jQuery.noConflict ();
slolife le
2
Je l' ai modifié la ligne script.textContent = "(" + callback.toString() + ")();";à script.textContent = "jQuery.noConflict();(" + callback.toString() + ")();";mon propre modèle de script utilisateur donc il n'y aura pas de conflits surprenants. :)
RCE
1
@hippietrail Dans main(), vous pouvez utiliser $.loadScript(), puis exécuter tout le reste lorsque loadScript a fini de charger jQueryUI.
tghw
1
-1: Avec cette méthode, le code de mains'exécutera dans le contexte de la page cible, ce qui signifie que (entre autres) la politique de requête intersite de la page cible s'applique - (par exemple, j'ai dû réinjecter GM_xmlhttpRequestavant de voir que c'était le cas ne m'aidez pas). À la fin, j'ai simplement copié-collé le code de jquery.min.js.
Jean Hominal
43

J'ai écrit quelques fonctions basées sur le script d'Erik Vold pour m'aider à exécuter des fonctions, du code et d'autres scripts dans un document. Vous pouvez les utiliser pour charger jQuery dans la page, puis exécuter du code sous la windowportée globale .

Exemple d'utilisation

// ==UserScript==
// @name           Example from http://stackoverflow.com/q/6834930
// @version        1.3
// @namespace      http://stackoverflow.com/q/6834930
// @description    An example, adding a border to a post on Stack Overflow.
// @include        http://stackoverflow.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");
});

Vous pouvez cliquer ici pour l'installer, si vous croyez que je n'essaye pas de vous inciter à installer quelque chose de malveillant et que personne n'a modifié mon message pour pointer vers autre chose. Rechargez la page et vous devriez voir une bordure autour de mon message.

Les fonctions

load(url, onLoad, onError)

Charge le script à urldans le document. En option, des rappels peuvent être fournis pour onLoadet onError.

execute(functionOrCode)

Insère une fonction ou une chaîne de code dans le document et l'exécute. Les fonctions sont converties en code source avant d'être insérées, elles perdent donc leur portée / fermetures actuelles et sont exécutées sous la windowportée globale .

loadAndExecute(url, functionOrCode)

Un raccourci; cela charge un script à partir de url, puis l'insère et s'exécute en functionOrCodecas de succès.

Code

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); });
}
Jeremy Banks
la source
@cyphunk Oui, il était crucial pour moi de sauvegarder ces quelques personnages. En fait, je me sens assez stupide d'avoir laissé ce post en l'utilisant si inutilement. Je vais le supprimer.
Jeremy Banks
18

Utilisez jQuery sans crainte de conflits , en appelant jQuery.noConflict(true). Ainsi:

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);
}


Mais, pour les scripts multi-navigateurs, pourquoi ne pas profiter d'une belle copie locale rapide de jQuery, quand vous le pouvez?

Ce qui suit fonctionne comme un script utilisateur Chrome et un script Greasemonkey, et il utilise la belle @requirecopie locale de jQuery, si la plate-forme le prend en charge.

// ==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);
}
Brock Adams
la source
3
+1 Impressionnant, parmi toutes ces réponses, la vôtre est la seule qui dit d'utiliser @require, vous pouvez également utiliser $ = unsafeWindow.jQuerysi la page a jQuery (je n'ai testé cela que dans Tampermonkey).
AMK
Attention: certaines versions d'IE n'ont pas console.log () sauf si vous avez installé les outils de développement, donc le script plantait. Habituellement, vous ne découvrirez cela qu'une fois que vous aurez publié votre script au-delà des développeurs et des spécialistes du contrôle qualité.
Parsingphase
1
@Parsingphase, IE est à peu près NA ici. La dernière fois que j'ai vérifié, IE ne prend toujours pas en charge les scripts utilisateur (pas du tout?). Cela a-t-il changé avec IE 10?
Brock Adams
Brock - bon point, je ne parviens pas à tenir la matrice d'incompatibilité d'IE dans ma tête. Donc, pas directement applicable aux scripts utilisateur (bien qu'il semble que les gens essaient parfois d'implémenter une solution IE), mais plutôt un piège général.
Parsingphase
15

Si la page contient déjà jQuery, suivez simplement ce modèle:

// ==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);
Mottie
la source
Je ne pense pas que cela fonctionne parce que l'utilisateur n'a pas accès à la fenêtre des documents?
Christoph
@Christoph Cela fonctionne, j'ai et j'utilise toujours un usercript en utilisant cette méthode.
Mottie
1
Il s'agit en fait d'injecter le script greasemonkey dans la page. Donc, probablement contourne certaines des garanties de Greasemonkey.
Thymine
1
@Thymine J'ai remarqué que cette méthode injecte le script utilisateur dans des pages indésirables. J'ai dû envelopper la partie d'injection dans une ifinstruction qui vérifie le window.location.
Mottie
12

Le moyen simple consiste à utiliser le requiredmot-clé:

// @require     http://code.jquery.com/jquery-latest.js
Stiger
la source
Il est uniquement pris en charge par l'implémentation de l'extension.
user2284570
@ user2284570 Il est pris en charge par toutes les extensions userscript que j'ai pu trouver pour n'importe quel navigateur.
Matt M.
@MattM. Je veux dire que vous n avez pas besoin d installer une extension sur Opera et Chrome pour exécuter les scripts d utilisateur.
user2284570
7

Il existe un moyen très simple de se déplacer, y compris une copie complète des scripts jQuery pour Chrome lorsque ces scripts n'utilisent en fait aucune fonctionnalité privilégiée ( fonctions GM_ *, etc.) ...

Insérez simplement le script lui-même dans le DOM de la page et exécutez-le! La meilleure partie est que cette technique fonctionne aussi bien sur Firefox + Greasemonkey, vous pouvez donc utiliser le même script pour les deux:

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();
}

(volé sans excuse de Shog9 sur meta.stackoverflow car il ne l'a pas déplacé ici, et je dois supprimer le méta-post ..)

Jeff Atwood
la source
4

En outre, vous pouvez emballer votre script avec l'extension jQuery to Chrome. Consultez les scripts de contenu de Google Chrome .

Les extensions Chrome, contrairement aux scripts Greasemonkey, peuvent se mettre à jour automatiquement.

NVI
la source
2
oui, ce serait plus facile. Mais je préfère vraiment maintenir mon script via userscripts.org pour le moment, et ne pas créer de redondance avec le référentiel d'extensions google.
Alekc
4
Et le téléchargement sur Google Web Store coûte 5 $.
Camilo Martin
4

Solution plus simple: coupez et collez le contenu de jquery.min.js en haut de votre script utilisateur. Terminé.

J'ai trouvé divers problèmes avec les réponses recommandées. La solution addJQuery () fonctionne sur la plupart des pages mais a des bogues sur beaucoup. Si vous rencontrez des problèmes, copiez et collez simplement le contenu de jquery dans votre script.

cyphunk
la source
Ouais, je pense que cela a le plus de sens, car on peut même créer un petit script de construction qui génère une version chrome en faisant exactement ce que vous suggérez ici.
dkinzer
2

Je me demande si vous ne pouvez pas vous fier document.defaultView.jQueryà votre script GM 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($);
}
gnarf
la source
1

Une autre approche serait de modifier votre script pour charger jQuery manuellement. Exemple de 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
}

EDIT: DRATS! Après le test, il semble que ce code ne fonctionne pas car Google Chrome exécute les scripts / extensions utilisateur dans une portée / un processus distinct de la page Web réelle. Vous pouvez télécharger le code jQuery à l'aide d'un XmlhttpRequest, puis l'évaluer, mais vous devez héberger le code sur un serveur qui autorise le partage de ressources inter-origines à l'aide de l'en- Access-Control-Allow-Origin: *tête. Malheureusement, AUCUN des CDN actuels avec jQuery ne le prend en charge.

Greg Bray
la source
-1

Extension parfaite pour intégrer jQuery dans Chrome Console aussi simple que vous pouvez l'imaginer. Cette extension indique également si jQuery a déjà été incorporé dans la page.

Cette extension permet d'intégrer jQuery dans n'importe quelle page de votre choix. Il permet d'utiliser jQuery dans le shell de la console (vous pouvez appeler la console Chrome par "Ctrl + Shift + j").

Pour intégrer jQuery dans l'onglet sélectionné, cliquez sur le bouton d'extension.

LIEN vers l'extension: https://chrome.google.com/extensions/detail/gbmifchmngifmadobkcpijhhldeeelkc

Andreï
la source
Ce n'est vraiment pas la réponse. Et pourquoi voudrais-je charger jQuery si je n'en avais pas besoin?
Vik