Skip to content
Advertisement

The script is not working in Mozilla Firefox

I need to send some data to the server when the page is closed or refreshed. Therefore, I created a simple script that can handle my needs. The problem is that this script is not working in Mozilla firefox.

The script is working in many other browsers like chrome, chromium, brave, opera, falkon, epiphany, qutebroser, Midori, safari, edge. The problem is only with firefox.

var timeLog = {
    start: null,
    end: null,

    init: function () {
        this.start = new Date().getTime();
    },

    sendResults: function () {
        this.end = new Date().getTime();
        var url = "tracker";
        url += "?" + "start=" + this.start;
        url += "&" + "end=" + this.end;
        url += "&" + "User-Agent-JS=" + navigator.userAgent;
        url += "&" + "url=" + window.location.toString();

        fetch(url, {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            keepalive: true
        });
    }
};

window.onbeforeunload = function () {
    timeLog.sendResults();
};

timeLog.init();

The error message is: Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource.

EDIT: if the event for onbeforeunload is registred as here:

window.onbeforeunload = async function(event){
    event.preventDefault();
    timeLog.sendResults();
};

it is working, but I need to confirm that I want to leave the page.

enter image description here

As I found on the internet, the problem arises because firefox uses its own implementation of fetch.

———————–SOLUTION [still not working in firefox correctly]————————-

window.onbeforeunload = function (event) {
    event.preventDefault();
    timeLog.sendResults();
    delete event['returnValue'];
};

———————–SOLUTION————————-

I used sendBeacon instead of fetch

so the final code is following:

/*      ----REPLACED----
        fetch(url, {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            keepalive: true
        });
*/        
        navigator.sendBeacon(url);

Advertisement

Answer

Let’s add a bit more code to see what is going on, allow the fetch to complete then process any error (pause to see it) then proceed to the unload if no error happened – our desired case.

var timeLog = {
  start: null,
  end: null,

  init: function() {
    this.start = new Date().getTime();
  },

  sendResults: function() {
    this.end = new Date().getTime();
    var url = "tracker";
    url += "?" + "start=" + this.start;
    url += "&" + "end=" + this.end;
    url += "&" + "User-Agent-JS=" + navigator.userAgent;
    url += "&" + "url=" + window.location.toString();

    return fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      keepalive: true
    });
  }
};

window.addEventListener('beforeunload', function(e) {
  // Cancel the event
  // e.preventDefault(); // If you prevent default behavior in Mozilla Firefox prompt will always be shown
  // Chrome requires returnValue to be set
  // e.returnValue = '';

  let myfetch = timeLog.sendResults();
  myfetch
    // borrowed code https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
    .then(response => {
      //do something with response
      const contentType = response.headers.get('content-type');
      if (!contentType || !contentType.includes('application/json')) {
        throw new TypeError("Oops, we haven't got JSON!");
      }
      return response.json();
    })
    .then(data => {
      /* process your data further */
    })
    .catch(error => {
      console.error(error);
      e.preventDefault(); // pause to see the error in console
    });
  // the absence of a returnValue property on the event will guarantee the browser unload happens
  delete e['returnValue'];
});

timeLog.init();
Advertisement