分享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 相关文章推荐
判断是否输入完毕再激活提交按钮
Jun 26 Javascript
jQuery的DOM操作之删除节点示例
Jan 03 Javascript
从零学jquery之如何使用回调函数
May 16 Javascript
jquery插件splitScren实现页面分屏切换模板特效
Jun 16 Javascript
jQuery根据元素值删除数组元素的方法
Jun 24 Javascript
深入了解JavaScript中的Symbol的使用方法
Jul 28 Javascript
基于canvas实现的钟摆效果完整实例
Jan 26 Javascript
JS实现拖动滚动条评分的效果代码分享
Sep 29 Javascript
数组Array的排序sort方法
Feb 17 Javascript
JavaScript设计模式之策略模式详解
Jun 09 Javascript
JavaScript的Object.defineProperty详解
Jul 09 Javascript
基于aotu.js实现微信自动添加通讯录中的联系人功能
May 28 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怎么实现网站保存快捷方式方便用户随时浏览
2013/08/15 PHP
基于linnux+phantomjs实现生成图片格式的网页快照
2015/04/15 PHP
PHP+mysql+ajax轻量级聊天室实现方法详解
2016/10/17 PHP
PHP开发的微信现金红包功能示例
2017/06/29 PHP
PHP仿tp实现mvc框架基本设计思路与实现方法分析
2018/05/23 PHP
Aster vs KG BO3 第一场2.19
2021/03/10 DOTA
js实现上传图片之上传前预览图片
2013/03/25 Javascript
node.js中的fs.lchown方法使用说明
2014/12/16 Javascript
jQuery实现可编辑的表格实例讲解(2)
2015/09/17 Javascript
深入浅出ES6新特性之函数默认参数和箭头函数
2016/08/01 Javascript
js获取当前页的URL与window.location.href简单方法
2017/02/13 Javascript
详解jQuery中的isPlainObject()使用方法
2018/02/27 jQuery
每个 JavaScript 工程师都应懂的33个概念
2018/10/22 Javascript
vue滚动插件better-scroll使用详解
2019/10/18 Javascript
Vue+ElementUI table实现表格分页
2019/12/14 Javascript
jQuery 选择器用法基础入门示例
2020/01/04 jQuery
vue使用原生swiper代码实例
2020/02/05 Javascript
JS面向对象编程基础篇(二) 封装操作实例详解
2020/03/03 Javascript
vue实现路由懒加载的3种方法示例
2020/09/01 Javascript
[04:27]DOTA2官方论坛水友赛集锦
2013/09/16 DOTA
python使用xlrd模块读写Excel文件的方法
2015/05/06 Python
全面解析Python的While循环语句的使用方法
2015/10/13 Python
Python 爬虫模拟登陆知乎
2016/09/23 Python
windows下python连接oracle数据库
2017/06/07 Python
python实现任意位置文件分割的实例
2018/12/14 Python
python+opencv实现摄像头调用的方法
2019/06/22 Python
python 用 xlwings 库 生成图表的操作方法
2019/12/22 Python
python 使用paramiko模块进行封装,远程操作linux主机的示例代码
2020/12/03 Python
销售文员的岗位职责
2013/11/20 职场文书
2014年设备管理工作总结
2014/11/26 职场文书
公务员年度考核登记表个人总结
2015/02/12 职场文书
采购员岗位职责范本
2015/04/07 职场文书
国家助学贷款承诺书
2015/04/30 职场文书
宣传稿格式范文
2015/07/23 职场文书
Python爬虫之爬取二手房信息
2021/04/27 Python
一篇文章看懂MySQL主从复制与读写分离
2021/11/07 MySQL