分享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 相关文章推荐
测试你的JS的掌握程度的代码
Dec 09 Javascript
使用dynatrace-ajax跟踪JavaScript的性能
Apr 12 Javascript
AngularJS ng-blur 指令详解及简单实例
Jul 30 Javascript
Three.js学习之正交投影照相机
Aug 01 Javascript
利用React-router+Webpack快速构建react程序
Oct 27 Javascript
基于bootstrap的选择框插件icheck
Dec 23 Javascript
如何理解jQuery中的ajaxSubmit方法
Mar 13 Javascript
详解vuex 中的 state 在组件中如何监听
May 23 Javascript
JavaScript中关于class的调用方法
Nov 28 Javascript
vue 内置过滤器的使用总结(附加自定义过滤器)
Dec 11 Javascript
JavaScript写个贪吃蛇小游戏(超详细)
Mar 17 Javascript
jQuery实现日历效果
Sep 11 jQuery
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自动跳转中英文页面
2008/07/29 PHP
PHP输出缓存ob系列函数详解
2014/03/11 PHP
PHP中date与gmdate的区别及默认时区设置
2014/05/12 PHP
php 使用file_get_contents读取大文件的方法
2014/11/13 PHP
MacOS 安装 PHP的图片裁剪扩展Tclip
2015/03/25 PHP
PHP实现根据数组某个键值大小进行排序的方法
2018/03/13 PHP
php 使用expat方式解析xml文件操作示例
2019/11/26 PHP
PHP超级全局变量【$GLOBALS,$_SERVER,$_REQUEST等】用法实例分析
2019/12/11 PHP
javascript 多种搜索引擎集成的页面实现代码
2010/01/02 Javascript
jQuery中将函数赋值给变量的调用方法
2012/03/23 Javascript
基于jquery的simpleValidate简易验证插件
2014/01/31 Javascript
javascript中拼接HTML字符串的最快、最好的方法
2014/06/07 Javascript
深入理解bootstrap框架之入门准备
2016/10/09 Javascript
简单实现jquery焦点图
2016/12/12 Javascript
JS中传递参数的几种不同方法比较
2017/01/20 Javascript
javascript实现多张图片左右无缝滚动效果
2017/03/22 Javascript
Angular中使用$watch监听object属性值的变化(详解)
2017/04/24 Javascript
详解Nodejs之静态资源处理
2017/06/05 NodeJs
详解Vue 2.0封装axios笔记
2017/06/22 Javascript
Vue项目利用axios请求接口下载excel
2020/11/17 Vue.js
[12:36]《DOTA2》国服注册与激活指南全攻略
2013/04/28 DOTA
[08:06]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Elephant 选手采访
2021/03/11 DOTA
Python中join和split用法实例
2015/04/14 Python
kaggle+mnist实现手写字体识别
2018/07/26 Python
python  创建一个保留重复值的列表的补码
2018/10/15 Python
python实现对象列表根据某个属性排序的方法详解
2019/06/11 Python
6种非常炫酷的CSS3按钮边框动画特效
2016/03/16 HTML / CSS
html5默认气泡修改的代码详解
2020/03/13 HTML / CSS
opencv实现图像平移效果
2021/03/24 Python
委托证明的格式
2014/01/10 职场文书
怎样写好自我评价呢?
2014/02/16 职场文书
信息管理与信息系统专业求职信
2014/06/21 职场文书
2015年五一劳动节演讲稿
2015/03/18 职场文书
退休欢送会致辞
2015/07/31 职场文书
初中体育课教学反思
2016/02/16 职场文书
Java网络编程之UDP实现原理解析
2021/09/04 Java/Android