分享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 相关文章推荐
二级域名或跨域共享Cookies的实现方法
Aug 07 Javascript
js中escape对应的C#解码函数 UrlDecode
Dec 16 Javascript
javascript面向对象之定义成员方法实例分析
Jan 13 Javascript
JS实现控制表格内指定单元格内容对齐的方法
Mar 30 Javascript
关于JavaScript数组你所不知道的3件事
Aug 24 Javascript
jquery广告无缝轮播实例
Jan 05 Javascript
深入浅析ES6 Class 中的 super 关键字
Oct 20 Javascript
详解tween.js 中文使用指南
Jan 05 Javascript
vue的mixins属性详解
Mar 14 Javascript
Vue表单绑定的实例代码(单选按钮,选择框(单选时,多选时,用 v-for 渲染的动态选项)
May 13 Javascript
解决Vue打包后访问图片/图标不显示的问题
Jul 25 Javascript
Webpack中SplitChunksPlugin 配置参数详解
Mar 24 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开发环境配置(MySQL数据库安装图文教程)
2010/04/28 PHP
ThinkPHP公共配置文件与各自项目中配置文件组合的方法
2014/11/24 PHP
Laravel 5 框架入门(二)构建 Pages 的管理功能
2015/04/09 PHP
基于jQuery的history历史记录插件
2010/12/11 Javascript
jquery动态分页效果堪比时光网
2014/09/25 Javascript
浅谈nodeName,nodeValue,nodeType,typeof 的区别
2015/01/13 Javascript
基于jquery步骤进度条源码分享
2015/11/12 Javascript
js判断上传文件后缀名是否合法
2016/01/28 Javascript
JavaScript模版引擎的基本实现方法浅析
2016/02/15 Javascript
深入理解jQuery事件绑定
2016/06/02 Javascript
浅谈Angular中ngModel的$render
2016/10/24 Javascript
ReactNative短信验证码倒计时控件的实现代码
2017/07/20 Javascript
Bootstrap实现下拉菜单多级联动
2017/11/23 Javascript
使用react实现手机号的数据同步显示功能的示例代码
2018/04/03 Javascript
Vue-cli assets SubDirectory及PublicPath区别详解
2020/08/18 Javascript
基于Ionic3实现选项卡切换并重新加载echarts
2020/09/24 Javascript
详解Python中的元组与逻辑运算符
2015/10/13 Python
利用Anaconda完美解决Python 2与python 3的共存问题
2017/05/25 Python
python运行其他程序的实现方法
2017/07/14 Python
Python实现希尔排序算法的原理与用法实例分析
2017/11/23 Python
Numpy掩码式数组详解
2018/04/17 Python
python读取word文档,插入mysql数据库的示例代码
2018/11/07 Python
对Python中创建进程的两种方式以及进程池详解
2019/01/14 Python
Python使用字典实现的简单记事本功能示例
2019/08/15 Python
pycharm中选中一个单词替换所有重复单词的实现方法
2020/11/17 Python
html5.2 dialog简介详解
2018/02/27 HTML / CSS
美国流行背包品牌:JanSport(杰斯伯)
2018/03/02 全球购物
时尚孕妇装:Ingrid & Isabel
2019/05/08 全球购物
Final类有什么特点
2012/04/25 面试题
高职教师岗位职责
2013/12/24 职场文书
人力资源部副职的竞聘演讲稿
2014/01/07 职场文书
红领巾广播站广播稿(3篇)
2014/09/20 职场文书
民事申诉状范本
2015/05/20 职场文书
干货!开幕词的写作方法
2019/04/02 职场文书
Python可视化学习之seaborn绘制矩阵图详解
2022/02/24 Python
Java 多线程协作作业之信号同步
2022/05/11 Java/Android