Question: If I navigate away from a page in the middle of an $.ajax() request it fires the error callback. I've tested in Safari and FF with both GET and POST requests.

One potential solution would be to abort all AJAX requests on page unload, but the error handler is called before unload, so this doesn't seem possible.

I want to be able to handle REAL errors such as 500s gracefully on the client side with a polite alert or a modal dialog, but I don't want this handling to be called when a user navigates away from the page.

How do I do this?

--

(Also strange: When navigating away from a page, the error handler says that the textStatus parameter is "error", the same it throws when receiving a 500/bad request.)

Answer: 
This is a tough one to handle correctly in all situations. Unfortunately in many popular browsers the `xhr.status` is the same (`0`) if the AJAX call is cancelled by navigation or by a server being down / unresponsive. So that technique rarely works.

Here's a set of highly "practical" hacks that I've accumulated that work fairly well in the majority of circumstances, but still isn't bullet-proof. The idea is to try to catch the navigate events and set a flag which is checked in the AJAX error handler. Like this:

```
var global_is_navigating = false;

$(window).on('beforeunload',function() {
    // Note: this event doesn't fire in mobile safari
    global_is_navigating = true;
});

$("a").on('click',function() {
    // Giant hack that can be helpful with mobile safari
    if( $(this).attr('href') ) {
        global_is_navigating = true;
    }
});

$(document).ajaxError(function(evt, xhr, settings) {
    // default AJAX error handler for page
    if( global_is_navigating ) {
        // AJAX call cancelled by navigation. Not a real error
        return;
    }
    // process actual AJAX error here.
});

```
Just wait a while before showing the error. If a request fails due to page unload, the timeout won't fire and the error won't be shown. My experiments have shown that 100ms is enough for desktop Safari and Firefox (desktop and Android) and 500ms is enough for iOS Safari. The durations are fuzzy, so you should increase the numbers if it's possible.

```js
const request = new XMLHttpRequest();

// Firefox calls onabort, Safari calls onerror
request.onabort = request.onerror = () => {
  // A short comprehensive solution
  setTimeout(() => {
    alert('The request has failed completely');
  }, 500);

  // A more gentle solution
  const isWebKit = 'ApplePayError' in window;
  const isDesktopSafari = !('standalone' in navigator);
  const isGecko = 'mozInnerScreenX' in window
  setTimeout(
    () => {
      alert('The request has failed completely');
    },
    isWebKit ? (isDesktopSafari ? 100 : 500) : (isGecko ? 100 : 0) 
  );
}

request.open('get', 'http://example.com', true);
request.send();

```

I've checked this solutions only in modern browsers (Chrome 87, Firefox 83, IE 11, Edge, Safari 14).