分享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 相关文章推荐
JavaScript Scoping and Hoisting 翻译
Jul 03 Javascript
SinaEditor使用方法详解
Dec 28 Javascript
js传中文参数controller里获取参数乱码问题解决方法
Jan 03 Javascript
基于jquery的文字向上跑动类似跑马灯的效果
Sep 22 Javascript
基于JavaScript实现一定时间后去执行一个函数
Dec 14 Javascript
Vue 父子组件、组件间通信
Mar 08 Javascript
如何解决vue与传统jquery插件冲突
Mar 20 Javascript
JavaScript中的一些隐式转换和总结(推荐)
Dec 22 Javascript
在 vue-cli v3.0 中使用 SCSS/SASS的方法
Jun 14 Javascript
webpack4 处理SCSS的方法示例
Sep 03 Javascript
js中事件对象和事件委托的介绍
Jan 21 Javascript
微信小程序实现多图上传
Jun 19 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学习笔记 数组的常用函数
2011/06/13 PHP
php断点续传之如何分割合并文件
2014/03/22 PHP
ThinkPHP整合百度Ueditor图文教程
2014/10/21 PHP
PHP中创建和验证哈希的简单方法实探
2015/07/06 PHP
CI(CodeIgniter)框架视图中加载视图的方法
2017/03/24 PHP
为Plesk PHP7启用Oracle OCI8扩展方法总结
2019/03/29 PHP
jQuery Ajax使用 全解析
2010/12/15 Javascript
Js数组的操作push,pop,shift,unshift等方法详细介绍
2012/12/28 Javascript
JavaScript 实现类的多种方法实例
2013/05/01 Javascript
js中cookie的添加、取值、删除示例代码
2013/10/21 Javascript
JS实现的倒计时效果实例(2则实例)
2015/12/23 Javascript
Vuejs第十三篇之组件——杂项
2016/09/09 Javascript
快速掌握jQuery插件WebUploader文件上传
2016/11/07 Javascript
详解vue中使用vue-quill-editor富文本小结(图片上传)
2019/04/24 Javascript
JavaScript常用工具函数汇总(浏览器环境)
2020/09/17 Javascript
vue 避免变量赋值后双向绑定的操作
2020/11/07 Javascript
NodeJS和浏览器中this关键字的不同之处
2021/03/03 NodeJs
[50:27]OG vs LGD 2018国际邀请赛淘汰赛BO3 第一场 8.26
2018/08/30 DOTA
简明 Python 基础学习教程
2007/02/08 Python
python中对list去重的多种方法
2014/09/18 Python
以Flask为例讲解Python的框架的使用方法
2015/04/29 Python
简单学习Python time模块
2016/04/29 Python
深入解析Python的Tornado框架中内置的模板引擎
2016/07/11 Python
Python列表推导式、字典推导式与集合推导式用法实例分析
2018/02/07 Python
python面向对象入门教程之从代码复用开始(一)
2018/12/11 Python
python学习——内置函数、数据结构、标准库的技巧(推荐)
2019/04/18 Python
Python实现验证码识别
2020/06/15 Python
Python descriptor(描述符)的实现
2020/11/15 Python
html5+css如何实现中间大两头小的轮播效果
2018/12/06 HTML / CSS
请解释一下webService? 如何用.net实现webService
2014/06/09 面试题
企业管理专业个人求职信范文
2013/09/24 职场文书
大学生第一学年自我鉴定
2014/09/12 职场文书
民事代理词范文
2015/05/25 职场文书
女方家长婚礼致辞
2015/07/27 职场文书
检讨书格式
2019/04/25 职场文书
PyTorch 如何检查模型梯度是否可导
2021/06/05 Python