分享JavaScript监听全部Ajax请求事件的方法

若Ajax请求是由jQuery的$.ajax发起的,认情况下可以使用 jQuery的Global Ajax Event Handlers监听到Ajax事件,然而我遇到的却是用原生JavaScript发起的Ajax请求,所以这种方法行不通。

然后呢,还有其他方法,比如说 Pub/Sub,但是这个发起请求的 js 代码我是无法改动的,也就不存在向代码添加 publish 的问题。同理,jQuery 的 .bind.trigger 也无法使用。

最后,决定使用直接 override XMLHttpRequest,同时配合使用自定义事件。

在 StackOverflow 上搜索,发现有个歪果仁给出了一个不靠谱的解决方法,嗯,贴出来给大家看看:

rush:js;"> ;(function () { var open = window.XMLHttpRequest.prototype.open,send = window.XMLHttpRequest.prototype.send,onReadyStateChange;

function openReplacement(method,url,async,user,password) {
// some code

return open.apply(this,arguments);
}

function sendReplacement(data) {
// some code

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

return send.apply(this,arguments);
}

function onReadyStateChangeReplacement() {
// some code

if (this._onreadystatechange) return this._onreadystatechange.apply(this,arguments);
}

window.XMLHttpRequest.prototype.open = openReplacement;
window.XMLHttpRequest.prototype.send = sendReplacement;
})();

这个解决方案,无法监听全部的 XHR Events ,而且 readystatechange 事件是在调用 send 方法后才监听,也就无法监听到 readyState = 1 时的事件。同时,如果在使用 send 方法后再对 onreadystatechange 设置回调函数,会将 override 代码又一次 override,也就无法产生预想的效果

那如何才能正确地 override XHR 呢?贴上代码,一起来看看:

rush:js;"> ;(function() { function ajaxEventTrigger(event) { var ajaxEvent = new CustomEvent(event,{ detail: this }); window.dispatchEvent(ajaxEvent); }

var oldXHR = window.XMLHttpRequest;

function newXHR() {
var realXHR = new oldXHR();

realXHR.addEventListener('abort',function () { ajaxEventTrigger.call(this,'ajaxAbort'); },false);

realXHR.addEventListener('error','ajaxError'); },false);

realXHR.addEventListener('load','ajaxLoad'); },false);

realXHR.addEventListener('loadstart','ajaxLoadStart'); },false);

realXHR.addEventListener('progress','ajaxProgress'); },false);

realXHR.addEventListener('timeout','ajaxTimeout'); },false);

realXHR.addEventListener('loadend','ajaxLoadEnd'); },false);

realXHR.addEventListener('readystatechange',function() { ajaxEventTrigger.call(this,'ajaxReadyStateChange'); },false);

return realXHR;
}

window.XMLHttpRequest = newXHR;
})();

这样,就为 XHR 添加自定义事件。如何调用

rush:js;"> var xhr = new XMLHttpRequest();

window.addEventListener('ajaxReadyStateChange',function (e) {
console.log(e.detail); // XMLHttpRequest Object
});
window.addEventListener('ajaxAbort',function (e) {
console.log(e.detail.responseText); // XHR 返回的内容
});

xhr.open('GET','info.json');
xhr.send();

需要注意的是,正常的 readystatechange 等事件 handler 返回的 eXMLHttpRequest 对象,但是自定义方法 ajaxReadyStateChange 等事件 handler 返回的 eCustomEvent 对象,而 e.detail 才是真正的 XMLHttpRequest 对象。而获得 Ajax 请求返回内容e.responseText 也需要修改e.detail.responseText

同时,addEventListener 方法必须挂载在 window 对象上,而不能是 XHR 实例上。

因为以上代码使用了 CustomEvent 构造函数,在现代浏览器上可以正常使用,但是在 IE 下,甚至连 IE 11 都不支持,所以需要加上 polyfill,变成这样:

rush:js;"> ;(function () { if ( typeof window.CustomEvent === "function" ) return false;

function CustomEvent ( event,params ) {
params = params || { bubbles: false,cancelable: false,detail: undefined };
var evt = document.createEvent( 'CustomEvent' );
evt.initCustomEvent( event,params.bubbles,params.cancelable,params.detail );
return evt;
}

CustomEvent.prototype = window.Event.prototype;

window.CustomEvent = CustomEvent;
})();
;(function () {
function ajaxEventTrigger(event) {
var ajaxEvent = new CustomEvent(event,false);

return realXHR;
}

window.XMLHttpRequest = newXHR;
})();

此时,就可以在 IE 9+、Chrome 15+、FireFox 11+、Edge、Safari 6.1+、Opera 12.1+ 上愉快地使用了,以上就是本文的全部内容,希望大家能够喜欢。

相关文章

IE6是一个非常老旧的网页浏览器,虽然现在很少人再使用它,但...
PHP中的count()函数是用来计算数组或容器中元素的个数。这个...
使用 AJAX(Asynchronous JavaScript and XML)技术可以在不...
Ajax(Asynchronous JavaScript and XML)是一种用于改进网页...
本文将介绍如何通过AJAX下载Excel文件流。通过AJAX,我们可以...
Ajax是一种用于客户端和服务器之间的异步通信技术。通过Ajax...