11

I'm making a webapp. Some of the files that it loads contain pure, static .JSON data. I am using jQuery.ajax() (jQuery 1.5.2) with dataType:'json' and cache:true. Everything works well and all files are loaded. I have also configured the cache manifest to cache these JSON files (in addition to css , js, and images). Everything works well there too - when a user visits my site all the files that I need are cached correctly (I have applicationCache event handlers that confirm all is well). I have tested everything in Google Chrome and I can see that all files (including the JSON ones) are cached. When I disconnect from the network, everything works (the jQuery ajax calls automagically fetch the cached JSON files).

Now I am trying to test this on mobile Safari (on actual iPad and the iPhone simulator). Everything works as expected when in Safari - the pages are cached and on subsequent visits with network disconnected, the cached files are used.

However, when I add the app to the Home Screen using "add to home screen" in Safari, the app starts up and all the .js, .css, and images work correctly, BUT my Ajax calls don't! They don't access the cached .json files. For each Ajax call the XMLHttpRequest.status is 0, .statusText is error, but getAllResponseHeaders() shows the same (correct) headers as in the functional app in Safari.

I don't get this. Is this a limitation/bug in jQuery's .ajax() or what? Any help is appreciated.

ampersand
  • 4,264
  • 2
  • 24
  • 32

2 Answers2

17

Well, I hate to have to answer my own question, but here is what I've done in case someone else runs into this problem:

Apparently, when trying to use $.ajax() in offline mode, the request successfully fetches the cached files, but it returns 0 on XHMLHttpRequest.status. Having read up on this, I have seen that a status code of 0 can be returned when the user is offline and even when requesting local files. However, a successful GET should report a status code between 200 and 300. Perhaps $.ajax() checks for a 200-300 status, otherwise the request is considered to have failed.

To get around this, I added code to my error handler for the $.ajax() request which checks the jqXHR.responseText.**length**. I know the correct number of characters in my JSON files, so if jqXHR.responseText.length is the same as this known value, I consider the request a success and continue loading the files. The only extra thing I have to do is add JSON.parse(jqXHR.responseText) in the error handler, because the data is not parsed on status code 0.

ampersand
  • 4,264
  • 2
  • 24
  • 32
  • I'm having this exact same problem. Beautifully diagnosed, thank you. – jammus May 06 '11 at 16:43
  • @jammus, jQuery 1.6 came out a few days ago, and perhaps this issue is resolved. I haven't tested it yet. Which jQuery are you using? – ampersand May 06 '11 at 19:49
  • Thank you very much for the post.I have the same problem. @ ampersand: jquery-1.6.1.min.js did not solve it. –  May 17 '11 at 09:46
  • @Johan, thanks for confirming. Apparently, the only workaround is to examine the response in the $.Ajax error handler. You can test for status==0 or test responseText to see if it contains what u expect. – ampersand May 17 '11 at 14:43
  • Thanks for this. I've been scratching my head! – Tim Niblett May 31 '11 at 14:53
  • I found this on the jquery site as a solution (worked for me): `if (window.navigator.standalone) jQuery.ajaxSetup({isLocal:true});` – pyramation Oct 22 '11 at 00:51
  • Here is the jQuery bug thread that pyramation mentioned: http://bugs.jquery.com/ticket/8412 – Robert Levy May 31 '12 at 01:09
0

Another way to go about this (especially if you're a jQuery fan) could be to use the jQ Ajax method and force no cache:

cache:false,

Obviously, ajax will fail to load server side data while offline so why not check if user's online and if so, go ahead and fetch data:

 if(navigator.onLine) { //if user's online

    var data = 'data=' + randomVar;

    $.ajax({
        url: "url.php",  
        type: "POST",
        timeout:45000, //45 secs        
        data: data,
        cache: false,
        error: function () { alert("error: 45 secs have passed since request was sent. No data was returned - too slow internet connection or some other random error."); },
        success: function (responseText) {
            // do whatever if successful
        }
    });


} // if online
Jonathan
  • 2,953
  • 3
  • 26
  • 37
  • 1
    The whole point here is to access manifest-cached files using applicationCache. Using `cache:false` will add a cache-busting timestamp onto the url. I think this may screw up applicationCache because the urls will be different from what is specified in the cache manifest. Also, a `true` value on navigator.onLine DOES NOT necessarily mean there is connectivity (for example, it can mean you are connected to a LAN, not necessarily to the Internet). It is therefore unreliable. See http://www.html5rocks.com/en/mobile/workingoffthegrid/ for other ideas for detecting connectivity (no affiliation). – ampersand Jun 28 '12 at 20:26
  • That's of course absolutely true but it works for me so I'm sure it will work for others as well. The navigator.onLine is just one simple way of illustrating the point i'm making, obviously there are other perhaps better ways. Again, just examples. – Jonathan Jun 28 '12 at 21:43