27

I need to build a feature into a shopping cart that uses AJAX to retrieve an updated copy of the template from the server when something changes (e.g. a product is removed). I cannot modify the server side code, or the JavaScript that makes the shopping cart work in the first place. (Not ideal I know, but that's how it is)

What I want to do is run my own JavaScript every time the cart updates. I want to know if it is possible to listen for AJAX calls, and run my code every time one is made.

BudgieInWA
  • 2,184
  • 1
  • 17
  • 31

5 Answers5

45

To follow all AJAX calls on an HTML doc, you can overwrite the XMLHttpRequest prototype. This way, you can watch for actions on methods of XMLHttpRequest objects.

Here's a small sample code :

var open = window.XMLHttpRequest.prototype.open,
    send = window.XMLHttpRequest.prototype.send,
    onReadyStateChange;

function openReplacement(method, url, async, user, password) {
    var syncMode = async !== false ? 'async' : 'sync';
    console.warn(
        'Preparing ' +
        syncMode +
        ' HTTP request : ' +
        method +
        ' ' +
        url
    );
    return open.apply(this, arguments);
}

function sendReplacement(data) {
    console.warn('Sending HTTP request data : ', data);

    if(this.onreadystatechange) {
        this._onreadystatechange = this.onreadystatechange;
    }
    this.onreadystatechange = onReadyStateChangeReplacement;

    return send.apply(this, arguments);
}

function onReadyStateChangeReplacement() {
    console.warn('HTTP request ready state changed : ' + this.readyState);
    if(this._onreadystatechange) {
        return this._onreadystatechange.apply(this, arguments);
    }
}

window.XMLHttpRequest.prototype.open = openReplacement;
window.XMLHttpRequest.prototype.send = sendReplacement;

With this sample, for every AJAX call you'll have a warning in the JavaScript console.

It's not jQuery script, but you can use jQuery inside as you want.

This solution probably won't work on IE 6 or older, but it works in FF, IE7+, Chrome, Opera, Safari...

brasofilo
  • 25,496
  • 15
  • 91
  • 179
Nicolas
  • 1,639
  • 1
  • 14
  • 12
  • Mad props, this is exactly what I was hoping for. I didn't know about the `apply` method and that's a clever way of overwriting the function while still referencing the original. I've long finished the project that required this, but I know! – BudgieInWA May 22 '11 at 04:11
  • silly question: how does one listen to the receive-event (onreadystate?) i cannot get this to work, but it would be the only event i need. your proposed code works like a charm - thanks for that! –  Jun 06 '11 at 11:10
  • @michapixel : To be able to catch readystate events, you can also override the "onreadystatechange" property. I'll edit my code to add this overriding – Nicolas Jun 10 '11 at 09:41
  • where would you put the code you want to run after the ajax call is completed? – user718229 Apr 20 '18 at 14:12
  • The code in the answer can produce an infinite callback loop if any instance of XMLHttpRequest is used to make more than a single request (`.onreadystatechange` calls `._onreadystatechange` calls `._onreadystatechange` ...). To avoid this, only shadow `.onreadystatechange` if it isn't already the callback you intend to replace it with. – Adelmar May 09 '18 at 18:18
  • The infinite loop can be because of you pasting this twice in the console. – Yash Ojha Jan 21 '21 at 20:25
12

I'd prefer this solution.

$(document).ajaxComplete(function(event,request, settings){
    // Your code here
});
Yuriy Yakubskiy
  • 539
  • 5
  • 6
4

My Friend You can do this very easily with Jquery (As You have told You Are using Jquery)

(For those who are not using they can drive in Jquery library code under ajax function to view native code :') )

$(document).bind("ajaxSend", function(){
   $("#loading").show();
 }).bind("ajaxComplete", function(){
   $("#loading").hide();
 });

This is an code snippet taken from jquery official api docs ( See Global Events Section)

https://api.jquery.com/Ajax_Events/

Ravinder Payal
  • 2,884
  • 31
  • 40
  • As far as I can tell, this only listens to AJAX calls made by jQuery itself. The AJAX requests that I want to react to in the question are made by some unrelated code. – BudgieInWA Sep 25 '15 at 08:06
  • yes you are right but how large is your file that contains unrelated code – Ravinder Payal Sep 25 '15 at 09:05
  • It's not mine. Fundamental to the question is the fact that I can't change the code that is making the requests. Also, another solution works. – BudgieInWA Sep 25 '15 at 10:38
  • This is nice, however I can't find a way to access and inspect the data that was sent through ajax. – Rolf Oct 10 '17 at 11:48
2

You cannot listen to it but you can use a periodic updater plugin. Look at the below:

http://plugins.jquery.com/plugin-tags/periodic-updater
Jinesh Parekh
  • 2,131
  • 14
  • 18
0

This takes the same approach of adding a callback in the XHR prototype, but without setting any new properties on the prototype or writing our own event chaining mechanism. I think this is less likely to introduce conflicts.

(function() {
  // Reference to the original prototype method we're overriding
  var originalOpen = XMLHttpRequest.prototype.open;

  // Override prototype.open to add a custom callback whenever a request is opened
  XMLHttpRequest.prototype.open = function() {
    this.addEventListener('loadend', customCallback);

    // Execute the original prototype.open without affecting its execution context
    originalOpen.apply(this, arguments);
  };

  // All instances of XMLHttpRequest will execute this callback once on readyState 4
  var customCallback = function () {
    // In this context, `this` refers to the XHR instance that just completed
    console.log(this);

    // Remove the invoking listener to prevent duping on multiple calls to .open
    this.removeEventListener('loadend', customCallback);
  }
}());

This won't work in IE <= 8 (no support for .addEventListener())

Adelmar
  • 2,073
  • 2
  • 20
  • 20