5

I'm trying to reproduce jQuery's functions ajaxComplete and ajaxStart without jQuery so that they could be used in any environment with no library dependencies (it's a special use case). These functions allow for an event listener to be called before and after any ajax request. In my example, I call them preAjaxListener and postAjaxListener.

I'm trying to accomplish it by hooking into the XMLHttpRequest object and overwriting/decorating open and send. Yes, I know this is dirty.

XMLHttpRequest.prototype.open = (function(orig){ return function(a,b,c){ this._HREF = b; // store target url return orig.apply(this, arguments); // call original 'open' function }; })(XMLHttpRequest.prototype.open); XMLHttpRequest.prototype.send = (function(orig){ return function(){ var xhr = this; _core._fireAjaxEvents('pre', xhr._HREF); // preAjaxListener fires var rsc = xhr.onreadystatechange || function(){}; // store the original onreadystatechange if it exists xhr.onreadystatechange = function(){ // overwrite with custom function try { if (xhr.readyState == 4){ _core._fireAjaxEvents('post', xhr._HREF); // postAjaxListneer should fire this.onreadystatechange = rsc; } } catch (e){ } return rsc.apply(this, arguments); // call original readystatechange function }; return orig.apply(this, arguments); // call original 'send' function }; })(XMLHttpRequest.prototype.send); 

I do not want to write wrapper functions to make ajax requests. I want to be able to hook into any ajax request made by any library (or with vanilla js) on the page.

So far, only the preAjaxListener function works. I can't seem to figure out why, but it seems that onreadystatechange is never being called. Any guidance would be greatly appreciated.

Working demo: http://jsfiddle.net/_nderscore/QTQ5s/

4
  • Why are you touching the native prototype? Create your own wrapper object. Commented Dec 19, 2013 at 18:55
  • 1
    Because I can! :) I know it's dirty, but I do not want to write wrapper functions to make ajax requests. I'm in a situation where there are scripts on the page which I have no control over and I want to be able to hook into any ajax request made by any library (or with vanilla js). Commented Dec 19, 2013 at 18:57
  • So you want to capture every XHR request/response on the page? Commented Dec 19, 2013 at 19:03
  • Yes, exactly. I want to be able to run a function before it's sent, and after a response comes back. Commented Dec 19, 2013 at 19:04

1 Answer 1

6

Using .onreadystatechange wasn't working because I was testing with jQuery and jQuery's ajax methods manipulate and removes the onreadystatechange property.

However, adding an event listener for loadend works just fine everywhere but IE. For IE, I set up an interval instead - not the optimal solution, but it works for my needs. I only intended this script to work on IE8+ and modern browsers.

XMLHttpRequest.prototype.send = (function(orig){ return function(){ _core._fireAjaxEvents('pre', this._HREF); if (!/MSIE/.test(navigator.userAgent)){ this.addEventListener("loadend", function(){ _core._fireAjaxEvents('post', this._HREF); }, false); } else { var xhr = this, waiter = setInterval(function(){ if(xhr.readyState && xhr.readyState == 4){ _core._fireAjaxEvents('post', xhr._HREF); clearInterval(waiter); } }, 50); } return orig.apply(this, arguments); }; })(XMLHttpRequest.prototype.send); 
Sign up to request clarification or add additional context in comments.

5 Comments

How can we use it?
I run this code and after ajax at page I see error in console: Uncaught ReferenceError: _core is not defined
at Google Chrome 50
@VitalyZdanevich You can take a look at my full fiddle example here: jsfiddle.net/_nderscore/QTQ5s _core is just a plain object which I was using as a placeholder in place of a library I was working on.
@nderscore very nice solution mate!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.