37

I'm trying to intercept all AJAX calls in order to check if that AJAX response contains specific error code that I send as JSON from my PHP script (codes: ACCESS_DENIED, SYSTEM_ERROR, NOT_FOUND).

I know one can do something like this:

$('.log').ajaxSuccess(function(e, xhr, settings) {
});

But - does this work only if "ajaxSuccess" event bubble up to .log div? Am I correct? Can I achieve what I want by binding "ajaxSuccess" event to document?

$(document).ajaxSuccess(function(e, xhr, settings) {
});

I can do this in either jQuery or raw JavaScript.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Stann
  • 13,518
  • 19
  • 65
  • 73

4 Answers4

80

If you're using jQuery, $.ajaxSuccess is a good option, but here's a generic option that will intercept XHR calls from all frameworks (I've tested it with ExtJS and jQuery - it should work even if multiple frameworks are loaded concurrently). It's been tested to work with IE8, Chrome and Firefox.

(function(XHR) {
    "use strict";
        
    var open = XHR.prototype.open;
    var send = XHR.prototype.send;

    XHR.prototype.open = function(method, url, async, user, pass) {
        this._url = url;
        open.call(this, method, url, async, user, pass);
    };

    XHR.prototype.send = function(data) {
        var self = this;
        var oldOnReadyStateChange;
        var url = this._url;
    
        function onReadyStateChange() {
            if(self.readyState == 4 /* complete */) {
                /* This is where you can put code that you want to execute post-complete*/
                /* URL is kept in this._url */
            }
        
            if(oldOnReadyStateChange) {
                oldOnReadyStateChange();
            }
        }
    
        /* Set xhr.noIntercept to true to disable the interceptor for a particular call */
        if(!this.noIntercept) {            
            if(this.addEventListener) {
                this.addEventListener("readystatechange", onReadyStateChange, false);
            } else {
                oldOnReadyStateChange = this.onreadystatechange; 
                this.onreadystatechange = onReadyStateChange;
            }
        }
    
        send.call(this, data);
    }
})(XMLHttpRequest);

I've posted a more specific example on github which intercepts AJAX calls and posts the AJAX call durations back to the server for statistical analysis.

useless
  • 1,876
  • 17
  • 18
Andrew Newdigate
  • 6,005
  • 3
  • 37
  • 31
  • 2
    Based on your implementation, I've published something on NPM that works with both requests and responses! https://github.com/slorber/ajax-interceptor – Sebastien Lorber Sep 19 '14 at 15:21
  • That's awesome. More recently than this answer, I've been using this technique for adding CSRF headers to some of our AJAX requests. In future I'll look at using your lib. – Andrew Newdigate Oct 14 '14 at 14:38
  • 1
    Just for anyone using this method and wants to access the response data of the ajax calls. You can get the response in the `onReadyStateChange()` method via `self.response`. – riik Oct 23 '15 at 14:36
  • 1
    How do I stop the script from intercepting a jquery.ajax call? Where do I have to insert the noIntercept value? – Nova Apr 02 '16 at 07:48
10

From http://api.jquery.com/ajaxSuccess/ :

Whenever an Ajax request completes successfully, jQuery triggers the ajaxSuccess event. Any and all handlers that have been registered with the .ajaxSuccess() method are executed at this time.

So the selector doesn't define the position where you are "catching" the event (because, honestly, ajax event by its nature doesn't start from a DOM element), but rather defines a scope to which the handling will be defaulted (i.e. this will poitn to that/those element(s)).

In summary - it should be exactly what you wish for

mkilmanas
  • 3,395
  • 17
  • 27
4

The best way, which I found https://lowrey.me/intercept-2/

const intercept = (urlmatch, callback) => {
  let send = XMLHttpRequest.prototype.send;
  XMLHttpRequest.prototype.send = function() {
    this.addEventListener('readystatechange', function() {
      if (this.responseURL.includes(urlmatch) && this.readyState === 4) {
        callback(this);
      }
    }, false);
    send.apply(this, arguments);
  };
};
-2

Try using Mockjax.js http://code.appendto.com/plugins/jquery-mockjax

It lets you hijack AJAX calls to the server and mock the location.