This is rather hacky, but you can override any property (and thus interpose any method call) of the XMLHttpRequest
prototype, as well as the function/constructor itself.
The most convenient thing to do in your case is probably to hook the XMLHttpRequest
function itself, and simply add an event listener there:
var originalXHR = XMLHttpRequest;
XMLHttpRequest = function()
{
var ret = new originalXHR();
ret.addEventListener('load', function()
{
console.log('Ajax request finished!');
});
return ret;
};
The control flow here goes like this:
- Call original function
- Modify return value
- Return modified value
This takes advantage of the fact that if in JavaScript a constructor returns a value, that value replaces the object created with new
.
In action:
// Interface code
var field = document.querySelector('input');
document.querySelector('button').addEventListener('click', function()
{
var xhr = new XMLHttpRequest();
// crossorigin.me because we need a CORS proxy for most external URLs
xhr.open("GET", 'https://crossorigin.me/' + field.value);
xhr.send(null);
});
// Hooking code
var originalXHR = XMLHttpRequest;
XMLHttpRequest = function()
{
var ret = new originalXHR();
ret.addEventListener('load', function(ev)
{
// var xhr = ev.target;
console.log('Ajax request finished!');
});
return ret;
};
<!-- Demo page: -->
URL: <input type="text" value="https://google.com"><br>
<button>Load via XHR</button>
Note that this only gives you a method to detect when an XHR has finished loading, and not control over its return value. From within the function that does console.log
, you do however have access to the XHR itself, via ev.target
.
You could also create hooking points to modify the loaded content before it reaches the code on the page, but that would require a couple more hooks, because listeners can be added in more than one way (.onload
, .onreadystatechange
, .addEventListener
, etc), but it would be doable if necessary.