Replace jQuery version of a page with Greasemonkey

Making a dev environment, using source-control and a written release checklist will save you much grief (and is a requirement for most paid work).
~~~

The best way to replace the jQuery version on an existing page (that you don't control) is to:

  1. Stop the page's native jQuery from loading.
  2. Create a <script> node, containing the jQuery version you want, that loads before any subsequent scripts that use jQuery.

Unfortunately, Greasemonkey cannot do #1. It can only tamper with the page's native jQuery after it has loaded -- An approach that might work, but that slows the page and risks race conditions.

So, I recommend a two-tool solution:

  1. Make sure your Firefox has the Adblock add-on (which is an excellent add-on to use irregardless) and/or the RequestPolicy add-on.

  2. Use Adblock or RequestPolicy to temporarily block the old jQuery.
    For example, for StackOverflow, block http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js. (Note that RequestPolicy allows you to limit URL blocking to only a specific site.)

  3. Then use Greasemonkey to load the desired version of jQuery (which it can do irregardless of Adblock or RequestPolicy settings) at the beginning of the page.

    For example, this script will upgrade Meta SO's jQuery to 1.6.2, in conjunction with the block from step 2:

    // ==UserScript==
    // @name            _upgrade jQuery
    // @include         http://meta.stackexchange.com/questions/*
    // @run-at          document-start
    // ==/UserScript==
    
    /*--- Important!
            (1) We need another add-on, besides Greasemonkey, to
                disable the old, undesired script.
            (2) The DOM is not available yet
                (@run-at == document-start).
            (3) We cannot use a loop to check for the DOM because
                loading is halted while the loop runs.
            (4) setTimeout() and setInterval() are not fast enough due
                to minimum interval clamping.  By the time they detect
                the DOM, scripts that we need to precede may have
                loaded.
            (5) Therefor, we use a "set Zero Timeout" function as
                explained by David Baron at
                    http://dbaron.org/log/20100309-faster-timeouts .
            (6) By the time FF reports that the `document.head` is
                available, several head elements have loaded!
                (Is this a bug?)
                That means that if any dependent scripts are loaded
                before we can inject our jQuery version, then we must
                also reload those dependent scripts.
    */
    
    //////  setZeroTimeout() implementation: BEGIN
    
    /*--- Only add setZeroTimeout to the window object, and hide
        everything else in a closure.
    */
    ( function () {
        var timeouts    = [];
        var messageName = "zero-timeout-message";
    
        /*--- Like setTimeout, but only takes a function argument.
            There's no time argument (always zero) and no arguments.
            You have to use a closure.
        */
        function setZeroTimeout(fn) {
            timeouts.push(fn);
            window.postMessage(messageName, "*");
        }
    
        function handleMessage(event) {
            if (event.source == window && event.data == messageName) {
                event.stopPropagation();
                if (timeouts.length > 0) {
                    var fn = timeouts.shift();
                    fn();
                }
            }
        }
    
        window.addEventListener ("message", handleMessage, true);
    
        // Add the one thing we want added to the window object.
        window.setZeroTimeout = setZeroTimeout;
    })();
    
    //////  setZeroTimeout() implementation: END
    
    /*--- Now wait for the DOM and then add our version of jQuery,
        first thing.
    */
    function SearchForDOM () {
    
        var targetNode;
        if (typeof document.head == "undefined")
            targetNode = document.querySelector ("head, body");
        else
            targetNode = document.head;
        if (targetNode) {
    
            var scriptNode      = document.createElement ("script");
            scriptNode.src      = 'http://ajax.googleapis.com/ajax/'
                                + 'libs/jquery/1.6.2/jquery.min.js';
            targetNode.appendChild (scriptNode);
    
            /*--- By the time FF reports that the head element is
                available, a key dependent script has loaded!
                So, we reload it here, so that it can run with jQuery
                available.
            */
            var scriptNode      = document.createElement ("script");
            scriptNode.src      = location.protocol
                                + '\/\/' + location.host
                                + '/content/js/stub.js?v=49f661361016';
            targetNode.appendChild (scriptNode);
        }
        else
            setZeroTimeout (SearchForDOM);
    }
    
    SearchForDOM ();
    


    Note: that due to limitations of JS, GM, and Firefox, it may be necessary to reload scripts that depend on jQuery too. (This is the case for Meta Stack Overflow, at the moment.)


I know you're asking about jQuery and Greasemonkey for doing this, but let me off a completely different alternative, Fiddler.

If you're testing a site out with a new version of jQuery and you actually want to test any breaking changes, etc...you want to test the site as it will be, whereas a JavaScript (greasemonkey)-based after-the-fact replacement isn't an accurate simulation.

With fiddler you can replace the jQuery file that the browser requests (without the browser knowing). This way you're actually testing it completely, you're straight up dropping the new version in the page, no in-page JS tricks which may not be at all accurate to get there (handlers already hooked up, load order, etc.)

Eric Law (creator of Fiddler) has an excellent blog post on how to do exactly this:

Swapping out JQuery with Fiddler

The post was written for swapping out the minified version with the full version (also handy!) or a newer version for debugging, be sure to note both uses...they're both very useful in saving some debug/testing time.