¿Cómo puedo usar jQuery en los scripts de Greasemonkey en Google Chrome?

157

Como algunos de ustedes saben, Google Chrome ha puesto algunas limitaciones severas en los scripts de Greasemonkey.

El cromo no soporta @require, @resource, unsafeWindow, GM_registerMenuCommand, GM_setValue, o GM_getValue.

Sin requerir, no puedo encontrar una manera de incluir la biblioteca jQuery en el script Greasemonkey en Google Chrome.

¿Alguien tiene algún consejo en este asunto?

Alekc
fuente
19
Vale la pena señalar que Google Chrome con Tampermonkey tiene soporte por @requireahora, que es un enfoque mucho más simple que los de las respuestas.
Steen Schütt
2
Tampermonkey también es compatible con unsafeWindow, que es muy bueno para las páginas que ya tienen jQuery. var $ = unsafeWindow.jQuery;
Tim Goodman
1
@requirefunciona muy bien en sitios donde no le preocupa entrar en conflicto con ninguna de las miles o millones de otras bibliotecas JS que se unen a $ al cargar. Sin embargo, si usted está escribiendo un guión para un sitio con $ para otra cosa, o peor aún escribir una secuencia de comandos para ejecutar en cada sitio cada vez, utilizar el mecanismo de carga relativamente seguro explica por tghw a continuación.
GDorn

Respuestas:

192

De "Consejo de usuario: Uso de jQuery - Blog de 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
fuente
En lugar de las 3 líneas dentro de addEventListener para 'load', no "callback ();" ¿solo trabajo?
crdx
66
Mi página ya incluye jQuery, pero parece que el código anterior todavía es necesario para usar jQuery en el script de usuario. Sin embargo, dos jQuery incluidas pueden causar un conflicto, por lo que la primera línea de su función main () podría ser jQuery.noConflict ();
slolife
2
He modificado la línea script.textContent = "(" + callback.toString() + ")();";que script.textContent = "jQuery.noConflict();(" + callback.toString() + ")();";en mi propia plantilla de scripts de usuario por lo que no habrá ningún conflicto sorprendentes. :)
RCE
1
@hippietrail En main(), puede usar $.loadScript(), y luego ejecutar todo lo demás cuando loadScript termine de cargar jQueryUI.
tghw
1
-1: con ese método, el código mainse ejecutará en el contexto de la página de destino, lo que significa que (entre otras cosas) se aplica la política de solicitud de sitios cruzados de la página de destino (por ejemplo, tuve que reinyectar GM_xmlhttpRequestantes de ver que sí lo hizo) no me ayudes) Al final, simplemente copio y pego el código de jquery.min.js.
Jean Hominal
43

He escrito algunas funciones basadas en el script de Erik Vold para ayudarme a ejecutar funciones de ejecución, código y otros scripts en un documento. Puede usarlos para cargar jQuery en la página y luego ejecutar código bajo el windowalcance global .

Ejemplo de uso

// ==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");
});

Puede hacer clic aquí para instalarlo, si confía en que no estoy tratando de engañarlo para que instale algo malicioso y que nadie haya editado mi publicación para señalar otra cosa. Vuelva a cargar la página y debería ver un borde alrededor de mi publicación.

Las funciones

load(url, onLoad, onError)

Carga el script urlen el documento. Opcionalmente, se pueden proporcionar devoluciones de llamada para onLoady onError.

execute(functionOrCode)

Inserta una función o cadena de código en el documento y lo ejecuta. Las funciones se convierten en código fuente antes de insertarse, por lo que pierden su alcance / cierre actual y se ejecutan debajo del windowalcance global .

loadAndExecute(url, functionOrCode)

Un atajo; Esto carga un script url, luego lo inserta y ejecuta functionOrCodesi tiene éxito.

Código

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
fuente
@cyphunk Sí, fue crucial para mí salvar esos pocos personajes. En realidad, me siento bastante tonto por haber dejado esta publicación utilizándola tan inútilmente. Lo quitaré
Jeremy Banks
18

Utilice jQuery sin temor a conflictos , llamando jQuery.noConflict(true). Al igual que:

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


Pero, para los scripts entre navegadores, ¿por qué no aprovechar una copia local agradable y rápida de jQuery, cuando puede?

Lo siguiente funciona como un script de usuario de Chrome y un script de Greasemonkey, y utiliza la buena @requirecopia local de jQuery, si la plataforma lo admite.

// ==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
fuente
3
+1 Impresionante, de todas estas respuestas la suya es la única que dice usar @require, también puede usarla $ = unsafeWindow.jQuerysi la página tiene jQuery (solo he probado esto en Tampermonkey).
AMK
Advertencia: algunas versiones de IE no tienen console.log () a menos que haya instalado herramientas de desarrollo, por lo que el script se bloqueará. Por lo general, descubrirá esto solo una vez que haya lanzado su script más allá de los desarrolladores y la gente de control de calidad.
Parsingphase
1
@Parsingphase, IE es prácticamente NA aquí. La última vez que lo verifiqué, IE todavía no es compatible con los scripts de usuario (¿en absoluto?). ¿Ha cambiado eso con IE 10?
Brock Adams
Brock: buen punto, no puedo mantener la matriz de incompatibilidad de IE en mi cabeza. Por lo tanto, no es directamente aplicable a los scripts de usuario (aunque parece que las personas ocasionalmente intentan implementar una solución de IE), pero es más un problema general.
Parsingphase
15

Si la página ya tiene jQuery, simplemente siga esta plantilla:

// ==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
fuente
¿No creo que esto funcione porque el script de usuario no tiene acceso a la ventana de documentos?
Christoph el
@ Christoph Funciona, tengo y todavía estoy usando un script de usuario con este método.
Mottie
1
En realidad, esto está inyectando el script greasemonkey en la página. Entonces, probablemente omita algunas de las garantías que tiene greasemonkey.
Thymine
1
@Thymine He notado que este método inyecta el script de usuario en páginas no deseadas. Tuve que ajustar la parte de inyección en una ifdeclaración que verifica el window.location.
Mottie
12

La manera simple es usar la requiredpalabra clave:

// @require     http://code.jquery.com/jquery-latest.js
Stiger
fuente
Solo es compatible con la implementación de la extensión.
user2284570
@ user2284570 Es compatible con todas las extensiones de script de usuario que puedo encontrar para cualquier navegador.
Matt M.
@MattM. Quiero decir que no tiene que instalar una extensión en Opera y Chrome para ejecutar los scripts de usuario.
user2284570
7

Hay una manera realmente fácil de moverse, incluida una copia completa de jQuery para los scripts de Chrome cuando esos scripts en realidad no usan ninguna característica privilegiada ( funciones GM_ *, etc.) ...

¡Simplemente inserta el script en la página DOM y ejecuta! La mejor parte es que esta técnica funciona igual de bien en Firefox + Greasemonkey, por lo que puede usar el mismo script para ambos:

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

(sin robo robado de Shog9 en meta.stackoverflow ya que no lo movió aquí, y tengo que eliminar la meta publicación ..)

Jeff Atwood
fuente
4

Además, puede empaquetar su script con la extensión jQuery to Chrome. Consulte las secuencias de comandos de contenido de Google Chrome .

Las extensiones de Chrome, a diferencia de los scripts de Greasemonkey, pueden actualizarse automáticamente.

NVI
fuente
2
Sí, sería más fácil. Pero realmente prefiero mantener mi script a través de userscripts.org por ahora, y no crear redundancia con el repositorio de extensiones de google.
Alekc
44
Y cuesta $ 5 cargarlo en Google Web Store.
Camilo Martin
4

Solución más fácil: corte y pegue el contenido de jquery.min.js en la parte superior de su script de usuario. Hecho.

Encontré varios problemas con las respuestas recomendadas. La solución addJQuery () funciona en la mayoría de las páginas pero tiene errores en muchas. Si tiene problemas, simplemente copie y pegue el contenido de jquery en su script.

cyphunk
fuente
Sí, creo que esto tiene más sentido, porque incluso se puede escribir un pequeño script de compilación que genere una versión de Chrome haciendo exactamente lo que sugieres aquí.
dkinzer
2

Me pregunto si no podrías confiar document.defaultView.jQueryen tu script GM:

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
fuente
1

Otro enfoque sería modificar su script para cargar jQuery manualmente. Ejemplo 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
}

EDITAR: DRATS! Después de probar, parece que este código no funciona, ya que Google Chrome ejecuta scripts / extensiones de usuario en un ámbito / proceso separado de la página web real. Puede descargar el código jQuery utilizando una XmlhttpRequest y luego evaluarlo, pero debe alojar el código en un servidor que permita el uso compartido de recursos de origen cruzado utilizando el Access-Control-Allow-Origin: *encabezado. Lamentablemente, NINGUNO de los CDN actuales con jQuery admite esto.

Greg Bray
fuente
-1

Extensión perfecta para incrustar jQuery en Chrome Console tan simple como puedas imaginar. Esta extensión también indoca si jQuery ya se ha incrustado en la página.

Esta extensión solía incrustar jQuery en cualquier página que desee. Permite usar jQuery en el shell de la consola (puede invocar la consola de Chrome con "Ctrl + Shift + j").

Para incrustar jQuery en la pestaña seleccionada, haga clic en el botón de extensión.

ENLACE a la extensión: https://chrome.google.com/extensions/detail/gbmifchmngifmadobkcpijhhldeeelkc

Andrey
fuente
Esa realmente no es la respuesta. ¿Y por qué querría cargar jQuery si no fuera necesario?
Vik