JavaScript detect an AJAX event
Asked Answered
N

3

85

Okay so basically I want to have a bit of javascript on a page that somehow attaches some kind of global event listener that can detect and do something if an ajax request is made (without directly calling it from the call), regardless of how the ajax call is made.

I figured out how to do this with jquery - if the ajax request is being done by jquery. Here is an example code for this:

$.post(
  // requested script
  'someScript.php', 
  // data to send
  {
    'foo' : 'bar',
    'a' : 'b'
  },
  // receive response
  function(response){
    // do something
  }
); // .post

// global event listener    
$(document).ajaxSend(
  function(event,request,settings){
    alert(settings.url); // output: "someScript.php"
    alert(settings.data); // output: "foo=bar&a=b"
  }
);

With this code, regardless of where/how I call $.post(..), the global event listener will trigger. It also works if I use $.get or $.ajax (any jquery ajax methods), regardless of how/when I call it (attached as an onclick, on page load, whatever).

However, I want to be able to extend this listener to trigger regardless of what js framework is used, or even if no framework is used. So for instance if I were to also have on a page the following code (generic ajax code from w3schools):

function loadXMLDoc()
{
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
    }
  }
xmlhttp.open("GET","test.txt",true);
xmlhttp.send();
}

And then I have on some random link an onclick call to that function, my global ajax event listener should be able to also detect this request. Well I added that code to my page and attached it to a random link's onclick (yes, the code itself works), but the jquery "global event listener" code above failed to detect that call.

I did some more digging and I know I can basically save the object to a temp object and overwrite the current object with a "wrapper" object that will call a specified function and then call the temp function, but this requires me to know ahead of time the original object being created/used. But I won't always know that ahead of time...

So...is this possible? Is there some way to detect an ajax get/post request was made, regardless of whether it was done with a generic object or from xyz framework? Or am I just going to have to make duplicate code to handle each framework and also know ahead of time the ajax object(s) being created/used?

edit:

I forgot to mention that it is not enough to detect that a request occurred. I also need to capture the data being sent in the request. The link provided in the comments below will help figure out if "a" request was made, but not get the data sent. p.s. - the answer provided in the link below is not very clear, at least to me anyways.

Norean answered 29/8, 2010 at 21:4 Comment(3)
possible duplicate of Figuring out when a XMLHttpRequest request was made without callbacksCecelia
Thanks for the link! This question is not entirely like that one, because I needed to also be able to capture the data being sent in the request (which I guess I neglected to include; edited in). However, it DID get me on the right track and I think I found a solution, will post when testing of it is complete. Thanks!Norean
I think what you are looking for is in this question about manipulating the prototype but in a better way https://mcmap.net/q/246341/-recreating-jquery-39-s-ajaxstart-and-ajaxcomplete-functionality/3462483Braggart
N
118

Okay this is what I have come up with so far:

<script type='text/javascript'>
var s_ajaxListener = new Object();
s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open;
s_ajaxListener.tempSend = XMLHttpRequest.prototype.send;
s_ajaxListener.callback = function () {
  // this.method :the ajax method used
  // this.url    :the url of the requested script (including query string, if any) (urlencoded) 
  // this.data   :the data sent, if any ex: foo=bar&a=b (urlencoded)
}

XMLHttpRequest.prototype.open = function(a,b) {
  if (!a) var a='';
  if (!b) var b='';
  s_ajaxListener.tempOpen.apply(this, arguments);
  s_ajaxListener.method = a;  
  s_ajaxListener.url = b;
  if (a.toLowerCase() == 'get') {
    s_ajaxListener.data = b.split('?');
    s_ajaxListener.data = s_ajaxListener.data[1];
  }
}

XMLHttpRequest.prototype.send = function(a,b) {
  if (!a) var a='';
  if (!b) var b='';
  s_ajaxListener.tempSend.apply(this, arguments);
  if(s_ajaxListener.method.toLowerCase() == 'post')s_ajaxListener.data = a;
  s_ajaxListener.callback();
}
</script>

DIRECTIONS:

Just c/p this onto your page or include it in a .js file or whatever. This will create an object called s_ajaxListener. Whenever an AJAX GET or POST request is made, s_ajaxListener.callback() is called, and the following properties are available:

s_ajaxListener.method : The ajax method used. This should be either GET or POST. NOTE: the value may not always be uppercase, it depends on how the specific request was coded. I'm debating the wisdom of automatically upper-casing it or leaving it to something else to toLowerCase() for a case-insensitive comparison.

s_ajaxListener.url : The url of the requested script (including query string, if any) (urlencoded). I have noticed, depending on how the data is sent and from which browser/framework, for example this value could end up being as " " or "+" or "%20". I am debating the wisdom of decoding it here or leave it to something else.

s_ajaxListener.data : the data sent, if any ex: foo=bar&a=b (same 'issue' as .url with it being url-encoded)

NOTES:

As it stands, this is not IE6 compatible. this solution is not quite good enough for me, as I want it to be IE6 compatible. But since a lot of other people don't care about IE6, I decided to post my solution in its current state, as it should work for you if you don't care about IE6.

I have tested this in (as of this posted date): Current Safari, Current Chrome, Current FireFox, IE8, IE8 (IE7 Compatible). It doesn't currently work with IE6 because IE6 uses an ActiveX object, while virtually everything else uses XMLHttpRequest.

Right now I don't have any clue how to, well basically prototype/extend/overload (?) ActiveXObject("Microsoft.XMLHTTP"). This is what I am currently researching...does anybody know offhand?

Under each of the browsers I tested above, this works with AJAX requests from a generic object, and also from the jquery and prototype frameworks. I know there are other frameworks out there, but IMO these 2 are the major ones. I might possibly QA MooTools, but other than that, I'm fine with only testing those.

If Anybody wants to contribute by testing and posting results about other browsers and/or frameworks, it would be appreciated :)

Norean answered 30/8, 2010 at 3:11 Comment(5)
You could define a wrapper function for IE6, like Sarissa does; have a look at Re-inventing XMLHttpRequest to see how this can be done (I don't have the time now). You can't extend an ActiveX object, but you can extend your own wrapper function that simply emulates the needed properties. I think most libraries test for the presence of window.XMLHttpRequest and your wrapper object will be detected.Cecelia
Also note that IE8 in IE7 compatibility mode ≠ IE7. I suspect that the XMLHttpRequest object is one of the things that differ: in IE7 it seems to be merely a wrapper function over an ActiveX component, whereas in IE8 it may be implemented as a 'real' XHR object.Cecelia
Currently doesn't work in FireFox. XMLHttpRequest.open.apply errors outRobson
Still works great, but for some reason XMLHttpRequest.send is being called on every click in Chrome, and the callback ends up being executed on every clickLunchroom
@PetrGazarov interesting.. I wonder if this is some side-effect of pre-fetching?Norean
D
6

For IE 6 compatibility, how about :

<script type='text/javascript'>
  var s_ajaxListener = new Object();

  // Added for IE support
  if (typeof XMLHttpRequest === "undefined") {
    XMLHttpRequest = function () {
      try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
      catch (e) {}
      try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
      catch (e) {}
      try { return new ActiveXObject("Microsoft.XMLHTTP"); }
      catch (e) {}
      throw new Error("This browser does not support XMLHttpRequest.");
    };
  }

  s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open;
  s_ajaxListener.tempSend = XMLHttpRequest.prototype.send;
  s_ajaxListener.callback = function () {
    // this.method :the ajax method used
    // this.url    :the url of the requested script (including query string, if any) (urlencoded) 
    // this.data   :the data sent, if any ex: foo=bar&a=b (urlencoded)
  }

  XMLHttpRequest.prototype.open = function(a,b) {
    if (!a) var a='';
    if (!b) var b='';
    s_ajaxListener.tempOpen.apply(this, arguments);
    s_ajaxListener.method = a;  
    s_ajaxListener.url = b;
    if (a.toLowerCase() == 'get') {
      s_ajaxListener.data = b.split('?');
      s_ajaxListener.data = s_ajaxListener.data[1];
    }
  }

  XMLHttpRequest.prototype.send = function(a,b) {
    if (!a) var a='';
    if (!b) var b='';
    s_ajaxListener.tempSend.apply(this, arguments);
    if(s_ajaxListener.method.toLowerCase() == 'post')s_ajaxListener.data = a;
    s_ajaxListener.callback();
  }
</script>
Dermal answered 14/5, 2014 at 13:48 Comment(1)
yeah that is pretty much what I did at the time.. but IE6 is something nobody should be supporting anymore, especially since Microsoft themselves stopped supporting it (by extension of not supporting XP) as of April 2014Norean
C
4

Having a need to listen to AJAX events by other scripts (either XHR and/or fetch) and inspired by this SO post, I have created a small utility that is quite versatile AjaxListener.js on GitHub (note: IE < 7 is NOT supported).

Basic Usage:

function mylistener(req, res)
{
    // API is either 'xhr' or 'fetch'
    console.log('REQUEST', 'API', req.getAPI(), 'Method', req.getMethod(), 'URL', req.getURL(), 'Headers', req.getHeaders(), 'Body', req.getBody());
    console.log('RESPONSE', 'API', res.getAPI(), 'Status', res.getStatus(), 'URL', res.getURL(), 'Headers', res.getHeaders(), 'Body', res.getBody());
}

AjaxListener.install().onRequest(mylistener);

//AjaxListener.onRequest(mylistener, false); // unlisten

//AjaxListener.uninstall(); // uninstall

Hope it is useful.

Chaudfroid answered 9/6, 2022 at 6:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.