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:
- 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".
 
- Just poll for URL changes.  It's simple and works well in practice, with fewer timing issues than all but technique #1. 
- If the site uses AJAX that changes the fragment (AKA "hash"), fire on the - hashchangeevent.  Alas, fewer and fewer AJAX sites do this.
 
- 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.
 
- Hack into the - history.pushStatefunction. 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.
}