Bagaimana saya bisa menggunakan skrip jQuery di Greasemonkey di Google Chrome?

157

Seperti yang mungkin Anda ketahui, Google Chrome telah membatasi beberapa skrip Greasemonkey.

Chromium tidak mendukung @require, @resource, unsafeWindow, GM_registerMenuCommand, GM_setValue, atau GM_getValue.

Tanpa memerlukan, saya tidak dapat menemukan cara untuk memasukkan perpustakaan jQuery dalam skrip Greasemonkey di bawah Google Chrome.

Apakah ada yang punya saran dalam hal ini?

Alekc
sumber
19
Perlu dicatat bahwa Google Chrome dengan Tampermonkey memiliki dukungan untuk @requiresaat ini, yang merupakan pendekatan yang jauh lebih sederhana daripada yang ada di jawaban.
Steen Schütt
2
Tampermonkey juga mendukung unsafeWindow, yang sangat bagus untuk halaman yang sudah memiliki jQuery. var $ = unsafeWindow.jQuery;
Tim Goodman
1
@requireberfungsi dengan baik di situs di mana Anda tidak khawatir tentang konflik dengan ribuan atau jutaan perpustakaan JS lainnya yang terikat pada $ pada saat memuat. Namun, jika Anda menulis skrip untuk situs menggunakan $ untuk sesuatu yang lain, atau lebih buruk lagi menulis skrip untuk dijalankan di setiap situs, gunakan mekanisme memuat yang relatif aman dijelaskan oleh tghw di bawah ini.
GDorn

Jawaban:

192

Dari "Tip Naskah Pengguna: Menggunakan jQuery - Blog 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
sumber
Alih-alih 3 baris di dalam addEventListener untuk 'load', tidak akan "callback ();" hanya bekerja?
crdx
6
Halaman saya sudah menyertakan jQuery, tetapi tampaknya kode di atas masih diperlukan untuk menggunakan jQuery di skrip users. Namun, dua jQuery termasuk dapat menyebabkan konflik, jadi baris pertama dari fungsi utama Anda () mungkin harus jQuery.noConflict ();
slolife
2
Saya telah memodifikasi baris script.textContent = "(" + callback.toString() + ")();";ke script.textContent = "jQuery.noConflict();(" + callback.toString() + ")();";dalam template skrip pengguna saya sendiri sehingga tidak akan ada konflik yang mengejutkan. :)
RCE
1
@hippietrail In main(), Anda dapat menggunakan $.loadScript(), dan menjalankan semuanya saat loadScript selesai memuat jQueryUI.
tghw
1
-1: Dengan metode itu, kode di mainakan dieksekusi dalam konteks halaman target, yang berarti bahwa (antara lain) kebijakan permintaan lintas-situs dari halaman target berlaku - (misalnya saya harus GM_xmlhttpRequestmemasukkan kembali sebelum melihat bahwa itu memang tidak membantu saya). Pada akhirnya saya cukup menyalin kode jquery.min.js.
Jean Hominal
43

Saya telah menulis beberapa fungsi berdasarkan skrip Erik Vold untuk membantu menjalankan saya menjalankan fungsi, kode, dan skrip lainnya dalam dokumen. Anda dapat menggunakannya untuk memuat jQuery ke halaman lalu menjalankan kode di bawah windowlingkup global .

Contoh Penggunaan

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

Anda dapat mengklik di sini untuk menginstalnya, jika Anda percaya bahwa saya tidak mencoba menipu Anda untuk menginstal sesuatu yang berbahaya dan tidak ada orang yang mengedit posting saya untuk menunjuk ke hal lain. Muat ulang halaman dan Anda akan melihat perbatasan di sekitar pos saya.

Fungsi

load(url, onLoad, onError)

Memuat skrip urlke dalam dokumen. Secara opsional, callback dapat disediakan untuk onLoaddan onError.

execute(functionOrCode)

Menyisipkan fungsi atau string kode ke dalam dokumen dan menjalankannya. Fungsi dikonversi ke kode sumber sebelum dimasukkan, sehingga kehilangan lingkup / penutupan saat ini dan dijalankan di bawah windowlingkup global .

loadAndExecute(url, functionOrCode)

Jalan pintas; ini memuat skrip dari url, lalu menyisipkan dan mengeksekusi functionOrCodejika berhasil.

Kode

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
sumber
@cyphunk Ya, sangat penting bagi saya untuk menyimpan beberapa karakter itu. Sebenarnya, saya merasa sangat bodoh karena telah meninggalkan posting ini menggunakannya tanpa tujuan. Saya akan menghapusnya.
Jeremy Banks
18

Gunakan jQuery tanpa takut konflik , dengan menelepon jQuery.noConflict(true). Seperti itu:

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


Tapi, Untuk skrip lintas-browser, mengapa tidak memanfaatkan salinan jQuery lokal yang bagus, cepat, kapan pun Anda bisa?

Berikut ini berfungsi sebagai skrip pengguna Chrome dan skrip Greasemonkey, dan ia menggunakan @requiresalinan jQuery lokal yang bagus , jika platform mendukungnya.

// ==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
sumber
3
+1 Mengesankan, dari semua jawaban ini, hanya jawaban Anda yang mengatakan untuk menggunakan @require, Anda juga dapat menggunakan $ = unsafeWindow.jQueryjika halaman tersebut memiliki jQuery (Saya baru menguji ini di Tampermonkey).
AMK
Peringatan: beberapa versi IE tidak memiliki console.log () kecuali Anda telah menginstal alat dev, sehingga skrip akan mogok. Biasanya Anda akan menemukan ini hanya setelah Anda merilis skrip Anda di luar pengembang dan QA rakyat.
Parsingphase
1
@ Parsingphase, IE cukup banyak di sini. Terakhir saya periksa, IE masih tidak mendukung script pengguna dengan baik (¿sama sekali?). Apakah itu berubah dengan IE 10?
Brock Adams
Brock - poin bagus, saya gagal memegang matriks ketidakcocokan IE di kepala saya. Jadi, tidak secara langsung berlaku untuk skrip pengguna (meskipun tampaknya orang kadang-kadang mencoba menerapkan solusi IE), tetapi lebih merupakan gotcha umum.
Parsingphase
15

Jika halaman sudah memiliki jQuery, cukup ikuti templat ini:

// ==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
sumber
Saya tidak berpikir ini berfungsi karena naskah pengguna tidak memiliki akses ke jendela dokumen?
Christoph
@Christoph Tidak berfungsi, saya miliki dan saya masih menggunakan userscript menggunakan metode ini.
Mottie
1
Ini sebenarnya menyuntikkan skrip greasemonkey ke halaman. Jadi mungkin melewati beberapa perlindungan yang dimiliki greasemonkey.
Thymine
1
@ Timine Saya telah memperhatikan bahwa metode ini memang menyuntikkan naskah pengguna ke halaman yang tidak diinginkan. Saya harus membungkus bagian suntikan dalam ifpernyataan yang memeriksa window.location.
Mottie
12

Cara sederhana menggunakan requiredkata kunci:

// @require     http://code.jquery.com/jquery-latest.js
Stiger
sumber
Itu hanya didukung oleh implementasi ekstensi.
user2284570
@ user2284570 Ini didukung oleh setiap ekstensi script pengguna yang dapat saya temukan untuk browser apa pun.
Matt M.
@MattM Maksud saya Anda tidak perlu menginstal ekstensi di Opera dan Chrome untuk menjalankan skrip pengguna.
user2284570
7

Ada cara yang sangat mudah untuk berkeliling termasuk salinan lengkap jQuery untuk skrip Chrome saat skrip tersebut tidak benar-benar menggunakan fitur istimewa (GM_ * fungsi, dll) ...

Cukup masukkan skrip itu sendiri ke halaman DOM dan jalankan! Bagian terbaiknya adalah teknik ini berfungsi dengan baik di Firefox + Greasemonkey, sehingga Anda dapat menggunakan skrip yang sama untuk keduanya:

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

(dicuri secara patologis dari Shog9 di meta.stackoverflow karena dia tidak memindahkannya di sini, dan saya harus menghapus posting meta ..)

Jeff Atwood
sumber
4

Anda juga dapat mengemas skrip Anda dengan ekstensi jQuery ke Chrome. Lihat Skrip Konten Google Chrome .

Ekstensi Chrome, tidak seperti skrip Greasemonkey, dapat memperbarui sendiri secara otomatis.

NVI
sumber
2
ya, itu akan lebih mudah. Tetapi saya benar-benar lebih suka mempertahankan skrip saya melalui userscripts.org untuk saat ini, dan tidak membuat redundansi dengan repositori ekstensi google.
Alekc
4
Dan biaya $ 5 untuk mengunggah ke Google Web Store.
Camilo Martin
4

Solusi yang lebih mudah: potong + rekatkan konten jquery.min.js ke bagian atas skrip pengguna Anda. Selesai

Saya menemukan berbagai masalah dengan jawaban yang disarankan. Solusi addJQuery () berfungsi di sebagian besar halaman tetapi memiliki banyak bug. Jika Anda mengalami masalah, cukup salin + rekatkan konten jquery ke skrip Anda.

cyphunk
sumber
Ya, saya pikir ini yang paling masuk akal, karena orang bahkan dapat membangunkan sedikit skrip build yang menghasilkan versi chrome dengan melakukan persis apa yang Anda sarankan di sini.
dkinzer
2

Saya ingin tahu apakah Anda tidak dapat bergantung pada document.defaultView.jQueryskrip GM Anda:

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

Pendekatan lain adalah memodifikasi skrip Anda untuk memuat jQuery secara manual. Contoh dari 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: DRAT! Setelah pengujian tampaknya kode ini tidak berfungsi karena Google Chrome menjalankan skrip pengguna / ekstensi dalam lingkup / proses terpisah dari halaman web yang sebenarnya. Anda bisa mengunduh kode jQuery menggunakan XmlhttpRequest dan kemudian Eval, tetapi Anda harus meng-host kode di server yang memungkinkan Berbagi Sumber Daya Silang- Menggunakan Access-Control-Allow-Origin: *header. Sayangnya tidak ada CDN saat ini dengan jQuery mendukung ini.

Greg Bray
sumber
-1

Ekstensi sempurna untuk menanamkan jQuery ke Chrome Console sesederhana yang Anda bayangkan. Ekstensi ini juga menyediakan jika jQuery sudah dimasukkan ke dalam halaman.

Ekstensi ini digunakan untuk menanamkan jQuery ke halaman mana pun yang Anda inginkan. Ini memungkinkan untuk menggunakan jQuery di shell konsol (Anda dapat memanggil konsol Chrome dengan "Ctrl + Shift + j").

Untuk menanamkan jQuery ke tab yang dipilih klik pada tombol extention.

LINK ke ekstensi: https://chrome.google.com/extensions/detail/gbmifchmngifmadobkcpijhhldeeelkc

Andrey
sumber
Itu benar-benar bukan jawabannya. Dan mengapa saya ingin memuat jQuery jika saya tidak perlu?
Vik