Improved jquery ajax error reporting

jQuery is a great library, and I highly recommend it after 6 months of use. One of the problems I’ve had with it, however, is its poor error reporting on ajax requests. While you can subscribe to the global event ajaxError, there’s nothing to tell you what went wrong. Specifically, I’ve wanted a way to report when the json parse fails.

Looking through the jQuery core, I found that the problem is that “status” field isn’t sent to the handler. A simple intervention on jQuery.handleError will close the gap:

jQuery.handleErrorOriginal = $.handleError;
jQuery.handleError = function( s, xhr, status, e ){
  if (s.global) {
    xhr.error_status = status;
  }
  jQuery.handleErrorOriginal.apply(this, arguments);
};

The “e” object is the original ajax configuration object. Since it reaches the global ajaxError listener, we can pull the status out of that.

Notice that I don’t replace the original functionality, I just do something before calling it with the same scope and arguments. The reason here is forwards-compatibility. Assuming that the name and api of jQuery.handleError doesn’t change, any changes to its internal functionality in future updates will likely be preserved without refactoring effort on your part.

To catch the errors:

$("html").ajaxError(function(xhr, s, e){
    var msg;
    switch (s.error_status) {
      case "parsererror":
        msg = "A JSON parsing error occurred.";
        break;
      case "timeout":
        msg = "An ajax request timed out.";
        break;
      default:
        msg = "An ajax error occurred.";
        break;
    }
    alert(msg + "\n\n" + e.url);
});

Why do I set xhr.error_status and then read s.error_status? Because, somewhere in the jumble of handleError and trigger, xhr becomes s. I love jQuery, but some of its core code can be quite difficult to read through.

jQuery(document).ready() bug with Google Chrome 4.0 beta

I came across a major issue with jQuery (1.3.2) and Google Chrome 4.0 beta today. I’d been testing my company’s web application (for which I’m the primary frontend developer) with Chrome 3.0x, and it was working fine. After a sudden influx of chrome bug reports from someone using 4, I updated to that. And suddenly, nearly nothing worked.

After a fair bit of futzing around in my code and the jQuery core library, I discovered that in chrome 4 in certain cases (probably in large apps), the DOMContentLoaded event was never sent to the core jQuery library. To test this, I inserted a couple of lines into the file you saw above:

3052  if ( document.addEventListener ) {
3053      // Use the handy event callback
[ins]     alert("jQ adding listener for DOMContentLoaded");
3054      document.addEventListener( "DOMContentLoaded", function(){
[ins]         alert("jQ received DOMContentLoaded event");
3055          document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
3056          jQuery.ready();
3057      }, false );

Both alerts show in FF, but only the first does in chrome. However, I found I could add my own and have them called.

Now, since the chances are you’re on this page because you googled for a workaround, here it is:

Google Chrome 4 beta jQuery document ready fix plugin

What this plugin does is first test to see if the browser is chrome 4, and if it is, then it adds another event listener for DOMContentLoaded, which will run even though the main jQuery one mysteriously does not. This calls jQuery.ready function (which is what is used to run through jQuery.readyList, the queued functions array).

We now have two chances for jQuery.ready to be called, therefore a potential race condition. To avoid this, I rewrote jQuery.ready() to null out  jQuery.readyList before cycling through the queued functions.

I still have no idea what’s causing Chrome 4 to fail on this. A test of document.readyState right after I added $(document).ready() shows that it is “loading”, so the problem isn’t that it finishes before adding the event handler or enqueueing the function.