分享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 相关文章推荐
ExtJS 简介 让你知道extjs是什么
Dec 29 Javascript
js removeChild 障眼法 可能出现的错误
Oct 06 Javascript
JavaScript 全角转半角部分
Oct 28 Javascript
利用javascript解决图片缩放及其优化的代码
May 23 Javascript
jQuery中获取Radio元素值的方法
Jul 02 Javascript
JS打开新窗口防止被浏览器阻止的方法
Jan 03 Javascript
JS绘制生成花瓣效果的方法
Aug 05 Javascript
浅谈使用splice函数对数组中的元素进行删除时的注意事项
Dec 04 Javascript
移动端日期插件Mobiscroll.js使用详解
Dec 19 Javascript
微信小程序实现的贪吃蛇游戏【附源码下载】
Jan 03 Javascript
js+html5 canvas实现ps钢笔抠图
Apr 28 Javascript
websocket4.0+typescript 实现热更新的方法
Aug 14 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 curl操作API接口类完整示例
2019/05/21 PHP
JQuery制作的放大效果的popup对话框(未添加任何jquery plugin)分享
2013/04/28 Javascript
利用JS进行图片的切换即特效展示图片
2013/12/03 Javascript
Javascript将数值转换为金额格式(分隔千分位和自动增加小数点)
2016/06/22 Javascript
AngularJS动态加载模块和依赖的方法分析
2016/11/08 Javascript
Bootstrap CSS组件之输入框组
2016/12/17 Javascript
Bootstrap fileinput文件上传组件使用详解
2017/06/06 Javascript
JS扩展String.prototype.format字符串拼接的功能
2018/03/09 Javascript
nodejs简单访问及操作mysql数据库的方法示例
2018/03/15 NodeJs
jQuery中的$是什么意思及 $. 和 $().的区别
2018/04/20 jQuery
vue addRoutes实现动态权限路由菜单的示例
2018/05/15 Javascript
vue.js过滤器+ajax实现事件监听及后台php数据交互实例
2018/05/22 Javascript
php结合js实现多条件组合查询
2019/05/28 Javascript
在vue中使用vuex,修改state的值示例
2019/11/08 Javascript
Vue开发中遇到的跨域问题及解决方法
2020/02/11 Javascript
Vue如何将页面导出成PDF文件
2020/08/17 Javascript
JavaScript字符串转数字的简单实现方法
2020/11/27 Javascript
WebPack工具运行原理及入门教程
2020/12/02 Javascript
Python 调用Java实例详解
2017/06/02 Python
Python 用Redis简单实现分布式爬虫的方法
2017/11/23 Python
python2.7无法使用pip的解决方法(安装easy_install)
2018/04/03 Python
对python中Matplotlib的坐标轴的坐标区间的设定实例讲解
2018/05/25 Python
django中模板的html自动转意方法
2018/05/27 Python
Python实现爬取马云的微博功能示例
2019/02/16 Python
python实现连连看辅助之图像识别延伸
2019/07/17 Python
python使用python-pptx删除ppt某页实例
2020/02/14 Python
Python基础之列表常见操作经典实例详解
2020/02/26 Python
Python实现检测文件的MD5值来查找重复文件案例
2020/03/12 Python
字符串str除首尾字符外的其他字符按升序排列
2013/03/08 面试题
大学生护理专业自荐信
2013/10/03 职场文书
大学军训感言1000字
2014/02/25 职场文书
办公设备采购方案
2014/03/16 职场文书
运动会演讲稿
2014/05/07 职场文书
旷工检讨书大全
2015/08/15 职场文书
《观潮》教学反思
2016/02/17 职场文书
详解Js模块化的作用原理和方案
2021/04/29 Javascript