Jak mogę używać jQuery w skryptach Greasemonkey w Google Chrome?

157

Jak niektórzy z was mogą wiedzieć, Google Chrome nałożył poważne ograniczenia na skrypty Greasemonkey.

Chromium nie obsługuje @require, @resource, unsafeWindow, GM_registerMenuCommand,GM_setValue , lub GM_getValue.

Bez wymagania nie mogę znaleźć sposobu na włączenie biblioteki jQuery do skryptu Greasemonkey w Google Chrome.

Czy ktoś ma jakąś radę w tej sprawie?

Alekc
źródło
19
Warto zauważyć, że Google Chrome z Tampermonkey ma na @requirerazie wsparcie , co jest znacznie prostszym podejściem niż te w odpowiedziach.
Steen Schütt
2
Tampermonkey obsługuje również unsafeWindow, co jest bardzo przydatne w przypadku stron, które już mają jQuery. var $ = unsafeWindow.jQuery;
Tim Goodman,
1
@requiredziała świetnie w witrynach, w których nie martwisz się o konflikt z żadną z tysięcy lub milionów innych bibliotek JS, które po załadowaniu wiążą się z $. Jeśli jednak piszesz skrypt dla witryny używającej $ do czegoś innego lub co gorsza piszesz skrypt, który będzie działał w każdej witrynie, użyj stosunkowo bezpiecznego mechanizmu ładowania opisanego poniżej.
GDorn

Odpowiedzi:

192

Z „Porada dotycząca skryptu użytkownika: Korzystanie z jQuery - Blog Erika Volda”

// ==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
źródło
Zamiast 3 wierszy wewnątrz addEventListener dla 'load', nie byłoby "callback ();" po prostu pracuj?
crdx
6
Moja strona zawiera już jQuery, ale wygląda na to, że powyższy kod jest nadal potrzebny do używania jQuery w skrypcie użytkownika. Jednak dwa dołączenia jQuery mogą powodować konflikt, więc pierwsza linia funkcji main () może potrzebować jQuery.noConflict ();
slolife
2
Mam zmodyfikowano linię script.textContent = "(" + callback.toString() + ")();";do script.textContent = "jQuery.noConflict();(" + callback.toString() + ")();";w moim szablonu skryptu użytkownika, więc nie będzie żadnych zaskakujących konflikty. :)
RCE
1
@hippietrail In main(), możesz użyć $.loadScript(), a następnie uruchomić wszystko inne, gdy loadScript zakończy ładowanie jQueryUI.
tghw
1
-1: W przypadku tej metody kod mainzostanie wykonany w kontekście strony docelowej, co oznacza, że ​​(między innymi) obowiązuje zasada żądań między witrynami strony docelowej - (np. Musiałem wprowadzić ponownie, GM_xmlhttpRequestzanim zobaczyłem, że tak się stało nie pomóż mi). W końcu po prostu skopiowałem wkleiłem kod jquery.min.js.
Jean Hominal,
43

Napisałem kilka funkcji opartych na skrypcie Erika Volda, które pomagają mi uruchamiać funkcje, kod i inne skrypty w dokumencie. Możesz ich użyć, aby załadować jQuery na stronę, a następnie uruchomić kod w ramach pliku globalwindow .

Przykładowe użycie

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

Możesz kliknąć tutaj aby go zainstalować, jeśli ufasz, że nie próbuję cię nakłonić do zainstalowania czegoś złośliwego i że nikt nie zmodyfikował mojego posta, aby wskazać na coś innego. Załaduj ponownie stronę i powinieneś zobaczyć ramkę wokół mojego postu.

Funkcje

load(url, onLoad, onError)

Ładuje skrypt urldo dokumentu. Opcjonalnie można przewidzieć wywołania zwrotne onLoadi onError.

execute(functionOrCode)

Wstawia funkcję lub ciąg kodu do dokumentu i wykonuje go. Funkcje są konwertowane na kod źródłowy przed wstawieniem, więc tracą swój obecny zakres / zamknięcia i są uruchamiane w ramach windowzakresu globalnego .

loadAndExecute(url, functionOrCode)

Skrót; to ładuje skrypt z url, a następnie wstawia i wykonuje, functionOrCodejeśli się powiedzie.

Kod

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
źródło
@cyphunk Tak, uratowanie tych kilku postaci było dla mnie kluczowe. Właściwie czuję się dość głupio, że opuściłem ten post, używając go tak bezcelowo. Usunę to.
Jeremy Banks
18

Używaj jQuery bez obawy o konflikty , dzwoniąc jQuery.noConflict(true). Tak jak to:

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


Ale w przypadku skryptów obsługujących różne przeglądarki, dlaczego nie skorzystać z ładnej, szybkiej, lokalnej kopii jQuery, kiedy tylko możesz?

Poniższe działa jako skrypt użytkownika Chrome i skrypt Greasemonkey i używa ładnej lokalnej @requirekopii jQuery, jeśli platforma ją obsługuje.

// ==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
źródło
3
+1 Imponujące, spośród wszystkich tych odpowiedzi Twoja jest jedyną, która mówi, aby użyć @require, możesz również użyć, $ = unsafeWindow.jQueryjeśli strona ma jQuery (przetestowałem to tylko w Tampermonkey).
AMK
Uwaga: niektóre wersje IE nie mają console.log (), chyba że zainstalowałeś narzędzia programistyczne, więc skrypt się zawiesił. Zwykle odkryjesz to dopiero, gdy opublikujesz swój skrypt poza programistami i osobami odpowiedzialnymi za kontrolę jakości.
Parsingphase
1
@Parsingphase, IE jest tutaj prawie NA. Ostatnio sprawdzałem, czy IE nadal nie obsługuje dobrze skryptów użytkownika (¿w ogóle?). Czy to się zmieniło w IE 10?
Brock Adams
Brock - słuszna uwaga, nie udaje mi się trzymać w głowie macierzy niekompatybilności IE. Tak więc nie ma bezpośredniego zastosowania do skryptów użytkownika (chociaż wydaje się, że ludzie czasami próbują wdrożyć rozwiązanie IE), ale bardziej ogólny problem.
Parsingphase
15

Jeśli strona ma już jQuery, po prostu postępuj zgodnie z tym szablonem:

// ==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
źródło
Nie sądzę, że to działa, ponieważ skrypt użytkownika nie ma dostępu do okna dokumentów?
Christoph
@Christoph To działa, mam i nadal używam skryptu użytkownika przy użyciu tej metody.
Mottie
1
W rzeczywistości jest to wstrzykiwanie skryptu greasemonkey do strony. Więc prawdopodobnie omija niektóre zabezpieczenia, które ma greasemonkey.
Tymine
1
@Thymine Zauważyłem, że ta metoda wstrzykuje skrypt użytkownika na niepożądane strony. Musiałem zawinąć część wstrzyknięcia w ifoświadczeniu, które sprawdza window.location.
Mottie
12

Prostym sposobem jest użycie requiredsłowa kluczowego:

// @require     http://code.jquery.com/jquery-latest.js
Stiger
źródło
Jest obsługiwany tylko przez implementację rozszerzenia.
user2284570
@ user2284570 Jest obsługiwany przez każde rozszerzenie skryptu użytkownika, jakie mogłem znaleźć dla dowolnej przeglądarki.
Matt M.
@MattM. Chodzi mi o to, że nie musisz instalować rozszerzenia w Operze i Chrome, aby uruchamiać skrypty użytkownika.
user2284570
7

Jest naprawdę łatwy sposób na obejście tego, w tym pełną kopię skryptów jQuery dla Chrome, gdy te skrypty w rzeczywistości nie używają żadnych uprzywilejowanych funkcji ( funkcje GM_ * itp.) ...

Po prostu wstaw sam skrypt do DOM strony i wykonaj! Najlepsze jest to, że ta technika działa równie dobrze w Firefoksie + Greasemonkey, więc możesz użyć tego samego skryptu do obu:

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

(bez przeprosin skradziony z Shog9 na meta.stackoverflow, ponieważ nie przeniósł go tutaj, a ja muszę usunąć meta post ..)

Jeff Atwood
źródło
4

Możesz również spakować swój skrypt z rozszerzeniem jQuery do Chrome. Zobacz skrypty zawartości przeglądarki Google Chrome .

Rozszerzenia Chrome, w przeciwieństwie do skryptów Greasemonkey, mogą się automatycznie aktualizować.

NVI
źródło
2
tak, byłoby łatwiej. Ale naprawdę wolę na razie utrzymywać mój skrypt przez userscripts.org i nie tworzyć nadmiarowości z repozytorium rozszerzeń Google.
Alekc,
4
A przesłanie do Google Web Store kosztuje 5 USD.
Camilo Martin
4

Łatwiejsze rozwiązanie: wytnij i wklej zawartość jquery.min.js w górnej części skryptu użytkownika. Gotowe.

Znalazłem różne problemy z zalecanymi odpowiedziami. Rozwiązanie addJQuery () działa na większości stron, ale na wielu ma błędy. Jeśli napotkasz problemy, po prostu skopiuj i wklej zawartość jquery do swojego skryptu.

cyphunk
źródło
Tak, myślę, że to ma największy sens, ponieważ można nawet napisać mały skrypt budujący, który generuje wersję Chrome, robiąc dokładnie to, co sugerujesz tutaj.
dkinzer,
2

Zastanawiam się, czy nie możesz polegać na document.defaultView.jQueryswoim skrypcie 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
źródło
1

Innym podejściem byłoby zmodyfikowanie skryptu w celu ręcznego załadowania jQuery. Przykład z 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
}

EDYCJA: DRATS! Po przetestowaniu okazuje się, że ten kod nie działa, ponieważ Google Chrome uruchamia skrypty użytkownika / rozszerzenia w innym zakresie / procesie niż rzeczywista strona internetowa. Możesz pobrać kod jQuery za pomocą XmlhttpRequest, a następnie Eval go, ale musisz hostować kod na serwerze, który umożliwia współdzielenie zasobów między źródłami przy użyciu Access-Control-Allow-Origin: *nagłówka. Niestety ŻADNA z obecnych sieci CDN z jQuery nie obsługuje tego.

Greg Bray
źródło
-1

Idealne rozszerzenie do osadzania jQuery w konsoli Chrome, tak proste, jak możesz sobie wyobrazić. To rozszerzenie wskazuje również, czy jQuery zostało już osadzone na stronie.

To rozszerzenie służyło do osadzania jQuery na dowolnej stronie. Pozwala na użycie jQuery w powłoce konsoli (konsolę Chrome można wywołać klawiszami „Ctrl + Shift + j”).

Aby osadzić jQuery w wybranej zakładce, kliknij przycisk rozszerzenia.

LINK do rozszerzenia: https://chrome.google.com/extensions/detail/gbmifchmngifmadobkcpijhhldeeelkc

Andrey
źródło
To naprawdę nie jest odpowiedź. Dlaczego miałbym chcieć ładować jQuery, gdybym nie musiał?
Vik