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


Posted in Javascript onAugust 28, 2016

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

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

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

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

;(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 呢?贴上代码,一起来看看:

;(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', function () { ajaxEventTrigger.call(this, 'ajaxError'); }, false);
 
  realXHR.addEventListener('load', function () { ajaxEventTrigger.call(this, 'ajaxLoad'); }, false);
 
  realXHR.addEventListener('loadstart', function () { ajaxEventTrigger.call(this, 'ajaxLoadStart'); }, false);
 
  realXHR.addEventListener('progress', function () { ajaxEventTrigger.call(this, 'ajaxProgress'); }, false);
 
  realXHR.addEventListener('timeout', function () { ajaxEventTrigger.call(this, 'ajaxTimeout'); }, false);
 
  realXHR.addEventListener('loadend', function () { ajaxEventTrigger.call(this, 'ajaxLoadEnd'); }, false);
 
  realXHR.addEventListener('readystatechange', function() { ajaxEventTrigger.call(this, 'ajaxReadyStateChange'); }, false);
 
  return realXHR;
 }
 
 window.XMLHttpRequest = newXHR;
})();

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

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,变成这样:

;(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, { 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', function () { ajaxEventTrigger.call(this, 'ajaxError'); }, false);
 
  realXHR.addEventListener('load', function () { ajaxEventTrigger.call(this, 'ajaxLoad'); }, false);
 
  realXHR.addEventListener('loadstart', function () { ajaxEventTrigger.call(this, 'ajaxLoadStart'); }, false);
 
  realXHR.addEventListener('progress', function () { ajaxEventTrigger.call(this, 'ajaxProgress'); }, false);
 
  realXHR.addEventListener('timeout', function () { ajaxEventTrigger.call(this, 'ajaxTimeout'); }, false);
 
  realXHR.addEventListener('loadend', function () { ajaxEventTrigger.call(this, 'ajaxLoadEnd'); }, false);
 
  realXHR.addEventListener('readystatechange', function() { ajaxEventTrigger.call(this, 'ajaxReadyStateChange'); }, false);
 
  return realXHR;
 }
 
 window.XMLHttpRequest = newXHR;
})();

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

Javascript 相关文章推荐
JSON 客户端和服务器端的格式转换
Aug 27 Javascript
Jquery之Ajax运用 学习运用篇
Sep 26 Javascript
在javascript中实现函数数组的方法
Dec 25 Javascript
文本域中换行符的替换示例
Mar 04 Javascript
JavaScript实现的图像模糊算法代码分享
Apr 22 Javascript
JavaScript实现MIPS乘法模拟的方法
Apr 17 Javascript
前端jquery部分很精彩
May 03 Javascript
关于Vue的路由权限管理的示例代码
Mar 06 Javascript
JavaScript实现构造json数组的方法分析
Aug 17 Javascript
Vue入门之数量加减运算操作示例
Dec 11 Javascript
Node.js实现批量下载图片简单操作示例
Jan 18 Javascript
在vue中实现嵌套页面(iframe)
Jul 30 Javascript
Node.js 日志处理模块log4js
Aug 28 #Javascript
node.js中 stream使用教程
Aug 28 #Javascript
ionic组件ion-tabs选项卡切换效果实例
Aug 27 #Javascript
郁闷!ionic中获取ng-model绑定的值为undefined如何解决
Aug 27 #Javascript
ionic实现带字的toggle滑动组件
Aug 27 #Javascript
ionic实现可滑动的tab选项卡切换效果
Apr 15 #Javascript
ionic实现滑动的三种方式
Aug 27 #Javascript
You might like
php session_start()关于Cannot send session cache limiter - headers already sent错误解决方法
2009/11/27 PHP
PHP随机数生成代码与使用实例分析
2011/04/08 PHP
php+ajax无刷新上传图片的实现方法
2016/12/06 PHP
PHP中number_format()函数的用法讲解
2019/04/08 PHP
解决javascript:window.close()在chrome,Firefox下失效的问题
2013/05/07 Javascript
jQuery的缓存机制浅析
2014/06/07 Javascript
javascript正则表达式总结
2016/02/29 Javascript
JavaScript中windows.open()、windows.close()方法详解
2016/07/28 Javascript
将Sublime Text 3 添加到右键中的简单方法
2017/12/12 Javascript
详解如何在微信小程序中愉快地使用sass
2018/07/30 Javascript
获取layer.open弹出层的返回值方法
2018/08/20 Javascript
JS实现判断有效的数独算法示例
2019/02/25 Javascript
泛谈JS逻辑判断选择器 || &&
2019/05/24 Javascript
vue2.0项目集成Cesium的实现方法
2019/07/30 Javascript
ant-design-vue 快速避坑指南(推荐)
2020/01/21 Javascript
理解JavaScript中的对象
2020/08/25 Javascript
[01:29:42]Liquid vs VP Supermajor决赛 BO 第一场 6.10
2018/07/05 DOTA
[01:02:30]Mineski vs Secret 2019国际邀请赛淘汰赛 败者组 BO3 第三场 8.22
2019/09/05 DOTA
python实现监控linux性能及进程消耗性能的方法
2014/07/25 Python
LRUCache的实现原理及利用python实现的方法
2017/11/21 Python
python机器学习之神经网络(二)
2017/12/20 Python
Python 实现12306登录功能实例代码
2018/02/09 Python
基于Django用户认证系统详解
2018/02/21 Python
基于python实现上传文件到OSS代码实例
2020/05/09 Python
pycharm 的Structure界面设置操作
2021/02/05 Python
解决CSS3 transition-delay 属性默认值0不带单位失效的问题
2020/10/29 HTML / CSS
自主招生自荐信格式
2013/12/03 职场文书
园林毕业生自我鉴定范文
2013/12/29 职场文书
会议邀请书范文
2014/02/02 职场文书
超市七夕促销活动方案
2014/08/28 职场文书
人身意外保险授权委托书
2014/10/01 职场文书
2014小学数学教研组工作总结
2014/12/06 职场文书
银行员工考核评语
2014/12/31 职场文书
平遥古城导游词
2015/02/03 职场文书
MySQL中使用or、in与union all在查询命令下的效率对比
2021/05/26 MySQL
OpenFeign实现远程调用
2022/08/14 Java/Android