14

I have an application that I am trying to create a Greasemonkey script for. It utilizes a lot of jQuery and AJAX to load content dynamically.

I've noticed that the URL does change each time I load a new item, even though the page doesn't refresh.

Is there a listener I can place on the page to relaunch the script each time the URL changes?

AsksAnyway
  • 890
  • 1
  • 8
  • 21
Sugitime
  • 1,818
  • 4
  • 23
  • 44

2 Answers2

21

How you do this depends on the site/application and on what you are trying to do. Here are your options, easiest and most robust first:

  1. Don't try to catch URL changes. Use calls to waitForKeyElements() to act on the parts of the various pages that you wanted to manipulate in the first place. This neatly handles a whole slew of timing issues inherent with all the other approaches.
    See also: "Choosing and activating the right controls on an AJAX-driven site".

  2. Just poll for URL changes. It's simple and works well in practice, with fewer timing issues than all but technique #1.

  3. If the site uses AJAX that changes the fragment (AKA "hash"), fire on the hashchange event. Alas, fewer and fewer AJAX sites do this.

  4. Use Mutation Observers to monitor changes to the <title> tag. Most AJAX pages are nice enough to change the title too. This may fire before your target content is loaded, though.

  5. Hack into the history.pushState function. This gives faster notice of page changes, BUT 95% of the time, it fires before your target elements have loaded. You will usually still need a timer. Plus, it brings in cross-scope problems, in a userscript environment.


For reference, here is a polling example. It's still the best, bare-bones, cross-browser, catch-all method:

/*--- Note, gmMain () will fire under all these conditions:
    1) The page initially loads or does an HTML reload (F5, etc.).
    2) The scheme, host, or port change.  These all cause the browser to
       load a fresh page.
    3) AJAX changes the URL (even if it does not trigger a new HTML load).
*/
var fireOnHashChangesToo    = true;
var pageURLCheckTimer       = setInterval (
    function () {
        if (   this.lastPathStr  !== location.pathname
            || this.lastQueryStr !== location.search
            || (fireOnHashChangesToo && this.lastHashStr !== location.hash)
        ) {
            this.lastPathStr  = location.pathname;
            this.lastQueryStr = location.search;
            this.lastHashStr  = location.hash;
            gmMain ();
        }
    }
    , 111
);

function gmMain () {
    console.log ('A "New" page has loaded.');
    // DO WHATEVER YOU WANT HERE.
}
Community
  • 1
  • 1
Brock Adams
  • 90,639
  • 22
  • 233
  • 295
  • It turns out sometimes this kicks in before the page has changed, so the solution is to put whatever you do in `gmMain()` in a `setTimeout()` (I use 500 milliseconds), on top of this, when you navigate back, the function gets called again, you can find a the container of the AJAX content, and add a class to the first element, and don't do anything if this classes exists. – GManz Jan 04 '14 at 09:35
-1

"I've noticed the URL does change each time" with a lot of AJAX would lead me that they are using the History API. If that's the case, take a look at window.onpopstate. That should fire on each URL change using the History API.

Steven V
  • 16,357
  • 3
  • 63
  • 76