分享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 跨域访问解决方案
Feb 14 Javascript
JavaScript Event学习第十章 一些可替换的事件对
Feb 10 Javascript
基于JQuery的模拟苹果桌面Dock效果(稳定版)
Oct 15 Javascript
javascript中创建对象的几种方法总结
Nov 01 Javascript
浅析jQuery中调用ajax方法时在不同浏览器中遇到的问题
Jun 11 Javascript
Bootstrap Table服务器分页与在线编辑应用总结
Aug 08 Javascript
全面了解函数声明与函数表达式、变量提升
Aug 09 Javascript
基于javascript实现数字英文验证码
Jan 25 Javascript
bootstrap模态框远程示例代码分享
May 22 Javascript
js 倒计时(高效率服务器时间同步)
Sep 12 Javascript
利用Angular2的Observables实现交互控制的方法
Dec 27 Javascript
Vue中fragment.js使用方法小结
Feb 17 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
兼容firefox,chrome的网页灰度效果
2011/08/08 PHP
php计算两个日期相差天数的方法
2015/03/14 PHP
PHP经典面试题集锦
2015/03/19 PHP
深入讲解PHP Session及如何保持其不过期的方法
2015/08/18 PHP
WordPress中获取指定分类及其子分类下的文章数目
2015/12/31 PHP
Yii框架学习笔记之应用组件操作示例
2019/11/13 PHP
Jquery实现视频播放页面的关灯开灯效果
2013/05/27 Javascript
深入理解Javascript中的循环优化
2013/11/09 Javascript
邮箱下拉自动填充选择示例代码附图
2014/04/03 Javascript
常用的jQuery前端技巧收集
2014/12/24 Javascript
JavaScript判断用户是否对表单进行了修改的方法
2015/03/18 Javascript
jQuery给多个不同元素添加class样式的方法
2015/03/26 Javascript
javascript检查某个元素在数组中的索引值
2016/03/30 Javascript
json定义及jquery操作json的方法
2016/10/03 Javascript
js中class的点击事件没有效果的解决方法
2016/10/13 Javascript
angularJS+requireJS实现controller及directive的按需加载示例
2017/02/20 Javascript
Bootstrap入门教程一Hello Bootstrap初识
2017/03/02 Javascript
Node.js Mongodb 密码特殊字符 @的解决方法
2017/04/11 Javascript
vue如何引用其他组件(css和js)
2017/04/13 Javascript
浅谈vue路径优化之resolve
2017/10/13 Javascript
解决vue 中 echart 在子组件中只显示一次的问题
2018/08/07 Javascript
微信小程序textarea层级过高的解决方法
2019/03/04 Javascript
iphone刘海屏页面适配方法
2019/05/07 Javascript
es6函数name属性功能与用法实例分析
2020/04/18 Javascript
python处理文本文件并生成指定格式的文件
2014/07/31 Python
讲解Python中运算符使用时的优先级
2015/05/14 Python
深入讲解Java编程中类的生命周期
2016/02/05 Python
python3使用pyqt5制作一个超简单浏览器的实例
2017/10/19 Python
OpenCV+Python识别车牌和字符分割的实现
2019/01/31 Python
django项目环境搭建及在虚拟机本地创建django项目的教程
2019/08/02 Python
对Python中 \r, \n, \r\n的彻底理解
2020/03/06 Python
html5调用摄像头功能的实现代码
2018/05/07 HTML / CSS
什么是SCM(软件配置管理)
2014/08/16 面试题
2016大学生优秀志愿者事迹材料
2016/02/25 职场文书
Go 实现英尺和米的简单单位换算方式
2021/04/29 Golang
NoSQL优缺点与MongoDB数据库简介
2022/06/05 MongoDB