Solution Timeline

All versions (edits) of solutions to Dynamic image request based on url appear below in the order they were created. Comments that appear under revisions were those created when that particular revision was current.

To see the revision history of a single solution (with diffs), click on the solution number (ie. "#1") in the upper right corner of a solution revision below.

← Bounty Expand all edits

partner.js

var __script = document.createElement("a");
__script.href = document.currentScript.src;
function updateGIF(gifID) {
    var link = window.location;
    var browserURL = link.host + link.pathname + link.search + link.hash;

    var scriptDomain = __script.protocol + "//" + __script.host;

    var gifURL = scriptDomain + "/" + encodeURIComponent(browserURL) + ".gif";
    document.getElementById("gif").src = gifURL;
}

window.onload = function() {
    updateGIF("gif");
}

window.onhashchange = function() {
    updateGIF("gif");
}

This assumes there's an <img id="gif"> in the document whose src needs to be updated.

For SPA's, onhashchange gets triggered when the part of the URL after # is updated.

Note: document.currentScript is supported by IE Edge, but not by IE 11 and lower.

;(function (window){
  function updateURL(){
    image.src = domain + encodeURIComponent(location.href.replace(location.protocol + '//', ''));
  }

  var domain = '//pixeldomain.com/',
      document = window.document,
      location = window.location,      
      current = document.currentScript || document.scripts[document.scripts.length-1],
      image = new Image();

  image.onload = function(){
    current.parentNode.insertBefore(image, current);
    image.onload = null;
  }

  window.addEventListener('popstate', updateURL, false);

  updateURL();
}(window));

I think this should work

!function(window, history, location) {
    const remote = 'https://pixeldomain.com/';

    const pushState = history.pushState;
    history.pushState = function(state) {
            if (typeof history.onpushstate == "function") {
                    history.onpushstate({state: state});
            }
            // ... whatever else you want to do
            // maybe call onhashchange e.handler
            return pushState.apply(history, arguments);
    };


    const log = () => {
        const current = encodeURIComponent(`${location.host}${location.pathname}${location.search}${location.hash}`);
        const url = `${remote}${current}.gif`;
        const gif = document.createElement('img');
        gif.src = url;
        console.log(`Report to ${url}`);
    }

    window.addEventListener('load', log);
    window.onhashchange = history.onpopstate = history.onpushstate = log;
}(window, window.history, window.location);

Here you have to modify the remote variable at the top of the code. This should work fine with regular apps
Angular, React, Vue apps as you can see it not only listens to hashchange but listens also to HTML5 history change event.

Also, the gif is not inserted into the page so it won't update the DOM and will be a little bit faster. The browser automatically fetches the image even if it's not inserted into the page.

Tell me if you need something else or doesn't work as expected. For example, if you want I could change the part where you have to manually put the remote url, and make it recognize itself based on the <script src=""> value

I think this should work

!function(window, history, location) {
    const remote = 'https://pixeldomain.com/';

    const pushState = history.pushState;
    history.pushState = function(state) {
            if (typeof history.onpushstate == "function") {
                    history.onpushstate({state: state});
            }
            // ... whatever else you want to do
            // maybe call onhashchange e.handler
            return pushState.apply(history, arguments);
    };


    const log = () => {
        const current = encodeURIComponent(`${location.host}${location.pathname}${location.search}${location.hash}`);
        const url = `${remote}${current}.gif`;
        const gif = document.createElement('img');
        gif.src = url;
        console.log(`Report to ${url}`);
    }

    window.addEventListener('load', log);
    window.onhashchange = history.onpopstate = history.onpushstate = log;
}(window, window.history, window.location);

Here you have to modify the remote variable at the top of the code. This should work fine with regular apps
Angular, React, Vue apps as you can see it not only listens to hashchange but listens also to HTML5 history change event.

Also, the gif is not inserted into the page so it won't update the DOM and will be a little bit faster. The browser automatically fetches the image even if it's not inserted into the page.

Tell me if you need something else or doesn't work as expected. For example, if you want I could change the part where you have to manually put the remote url, and make it recognize itself based on the <script src=""> value.

Edit: Just to clarify, here, the history.pushState method is overwriten because if you use that method (and popular frameworks routing systems do), the popstate event won't be triggered. But it will be triggered now thanks to the rewrite

I think this should work

!function(window, history, location) {
    const remote = 'https://pixeldomain.com/';

    const pushState = history.pushState;
    history.pushState = function(state) {
            if (typeof history.onpushstate == "function") {
                    history.onpushstate({state: state});
            }
            // ... whatever else you want to do
            // maybe call onhashchange e.handler
            return pushState.apply(history, arguments);
    };


    const log = () => {
        const current = encodeURIComponent(`${location.host}${location.pathname}${location.search}${location.hash}`);
        const url = `${remote}${current}.gif`;
        const gif = document.createElement('img');
        gif.src = url;
    }

    window.addEventListener('load', log);
    window.onhashchange = history.onpopstate = history.onpushstate = log;
}(window, window.history, window.location);

Here you have to modify the remote variable at the top of the code. This should work fine with regular apps
Angular, React, Vue apps as you can see it not only listens to hashchange but listens also to HTML5 history change event.

Also, the gif is not inserted into the page so it won't update the DOM and will be a little bit faster. The browser automatically fetches the image even if it's not inserted into the page.

Tell me if you need something else or doesn't work as expected. For example, if you want I could change the part where you have to manually put the remote url, and make it recognize itself based on the <script src=""> value.

Edit: Just to clarify, here, the history.pushState method is overwriten because if you use that method (and popular frameworks routing systems do), the popstate event won't be triggered. But it will be triggered now thanks to the rewrite

Explanation

Here is a solution that targets exactly your requirements without any overhead or unnecessary code. It supports SPAs and will generate the partner url without the protocol (http(s)) to unify the results in your tracking database.

How to use

Your partners will simple have to add the script tag before the closing </body> tag just like any other tracker (eg: Facebook Pixel or Google Analytics).

Code

;(function() {
    var pixelDomain = 'https://pixeldomain.com/';
    var pixel = document.createElement('img');

    updatePixelSource();
    document.body.appendChild(pixel);

    window.onpopstate = updatePixelSource.bind(this);
    window.onhashchange = updatePixelSource.bind(this);

    function updatePixelSource() {
        var partnerUrl = window.location.href.replace(window.location.protocol + '//', '');
        pixel.src = pixelDomain + encodeURIComponent(partnerUrl) + '.gif';
    }
})();

Code with documentation

// self executing code block with private scope to avoid conflicts
;(function() {
    // Domain containing the pixel gif
    var pixelDomain = 'https://pixeldomain.com/';

    // Html <img> tag generated by code
    var pixel = document.createElement('img');

    // Update the source of the img on load
    updatePixelSource();
    // Add the img to the page content to initialize the img load
    document.body.appendChild(pixel);

    // Add events to capture SPAs page change (old and new way of doing so)
    window.onpopstate = updatePixelSource.bind(this);
    window.onhashchange = updatePixelSource.bind(this);

    // Where the magic happens
    function updatePixelSource() {
        // Generate the partner url
        var partnerUrl = window.location.href.replace(window.location.protocol + '//', '');
        // Replace the <img> src with the encoded url and add .gif suffix
        pixel.src = pixelDomain + encodeURIComponent(partnerUrl) + '.gif';
    }

// end of self executing block and scope
})();

Wont you select a solution?