分享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小tip资料
Nov 23 Javascript
重构Javascript代码示例(重构前后对比)
Jan 23 Javascript
JS异常处理的一个想法(sofish)
Mar 14 Javascript
通过JS动态创建一个html DOM元素并显示
Oct 15 Javascript
JavaScript实现信用卡校验方法
Apr 07 Javascript
javascript学习笔记之函数定义
Jun 25 Javascript
使用postMesssage()实现iframe跨域页面间的信息传递
Mar 29 Javascript
基于jQuery实现动态搜索显示功能
May 05 Javascript
js 自带的 map() 方法全面了解
Aug 16 Javascript
Javascript之面向对象--接口
Dec 02 Javascript
angularJs中ng-model-options设置数据同步的方法
Sep 30 Javascript
JavaScript实现数字前补“0”的五种方法示例
Jan 03 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/09/16 PHP
解析PHP获取当前网址及域名的实现代码
2013/06/23 PHP
解析PHP中的file_get_contents获取远程页面乱码的问题
2013/06/25 PHP
微信支付开发教程(一)微信支付URL配置
2014/05/28 PHP
Laravel中任务调度console使用方法小结
2017/05/07 PHP
JavaScript 创建对象
2009/07/17 Javascript
Javascript 二维数组
2009/11/26 Javascript
Fixie.js 自动填充内容的插件
2012/06/28 Javascript
jquery教程限制文本框只能输入数字和小数点示例分享
2014/01/13 Javascript
javascript闭包传参和事件的循环绑定示例探讨
2014/04/17 Javascript
jquery向上向下取整适合分页查询
2014/09/06 Javascript
修复bash漏洞的shell脚本分享
2014/12/31 Javascript
js中判断变量类型函数typeof的用法总结
2016/08/09 Javascript
Javascript for in的缺陷总结
2017/02/03 Javascript
jQuery实现多张图片上传预览(不经过后端处理)
2017/04/29 jQuery
vue-cli 打包使用history模式的后端配置实例
2018/09/20 Javascript
一个因@click.stop引发的bug的解决
2019/01/08 Javascript
[01:01:13]2018DOTA2亚洲邀请赛 4.5 淘汰赛 Mineski vs VG 第三场
2018/04/06 DOTA
matplotlib设置legend图例代码示例
2017/12/19 Python
Python面向对象之继承和组合用法实例分析
2018/08/27 Python
使用Python获取网段IP个数以及地址清单的方法
2018/11/01 Python
用Anaconda安装本地python包的方法及路径问题(图文)
2019/07/16 Python
Python DataFrame一列拆成多列以及一行拆成多行
2019/08/06 Python
Django连接数据库并实现读写分离过程解析
2019/11/13 Python
一款纯css3实现简单的checkbox复选框和radio单选框
2014/11/05 HTML / CSS
美国办公用品购物网站:Quill.com
2016/09/01 全球购物
世界上最大的折扣香水店:FragranceNet.com
2016/10/26 全球购物
印尼最大的婴儿用品购物网站:Orami
2017/09/28 全球购物
英国皇室御用百货:福南梅森(Fortnum & Mason)
2017/12/03 全球购物
新加坡一家在线男士皮具品牌:Faire Leather Co.
2019/12/01 全球购物
体育专业个人的求职信范文
2013/09/21 职场文书
销售团队口号大全
2014/06/06 职场文书
2014年实习期工作总结
2014/11/27 职场文书
红楼梦读书笔记
2015/06/25 职场文书
Python机器学习之PCA降维算法详解
2021/05/19 Python
Python中字符串对象语法分享
2022/02/24 Python