分享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结合css实现网页换肤功能
Nov 02 Javascript
jquery统计复选框选中示例
Nov 05 Javascript
jQuery插件uploadify实现ajax效果的图片上传
Jun 18 Javascript
修改Jquery Dialog 位置的实现方法
Aug 26 Javascript
关于javascript原型的修改与重写(覆盖)差别详解
Aug 31 Javascript
jquery判断类型是不是number类型的实例代码
Oct 07 Javascript
JavaScript获取URL中参数querystring的方法详解
Oct 11 Javascript
Node 自动化部署的方法
Oct 17 Javascript
vue2.0+koa2+mongodb实现注册登录
Apr 10 Javascript
JS实现常见的查找、排序、去重算法示例
May 21 Javascript
node.js express框架简介与实现
Jul 23 Javascript
vue实现随机验证码功能(完整代码)
Dec 10 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 分页类(模仿google)-面试题目解答
2009/09/13 PHP
php URL验证正则表达式
2011/07/19 PHP
ThinkPHP模板之变量输出、自定义函数与判断语句用法
2014/11/01 PHP
php通过Chianz.com获取IP地址与地区的方法
2015/01/14 PHP
php使用iconv中文截断问题的解决方法
2015/02/11 PHP
javascript对象的property和prototype是这样一种关系
2007/03/24 Javascript
js 判断浏览器类型 去全角、半角空格 自动关闭当前窗口
2009/04/10 Javascript
Javascript 面向对象特性
2009/12/28 Javascript
JS中confirm,alert,prompt函数区别分析
2011/01/17 Javascript
一个支付页面DEMO附截图
2014/07/22 Javascript
node.js中的path.resolve方法使用说明
2014/12/08 Javascript
基于JavaScript实现表单密码的隐藏和显示出来
2016/03/02 Javascript
原生JavaScript制作微博发布面板效果
2016/03/11 Javascript
vue自定义指令实现v-tap插件
2016/11/03 Javascript
一种angular的方法级的缓存注解(装饰器)
2018/03/13 Javascript
Vue.js中对css的操作(修改)具体方式详解
2018/10/30 Javascript
Vue3.0 响应式系统源码逐行分析讲解
2019/10/14 Javascript
基于Vue中使用节流Lodash throttle详解
2019/10/30 Javascript
使用Vue 自定义文件选择器组件的实例代码
2020/03/04 Javascript
[02:22]《新闻直播间》2017年08月14日
2017/08/15 DOTA
[55:16]Mski vs VGJ.S Supermajor小组赛C组 BO3 第二场 6.3
2018/06/04 DOTA
跟老齐学Python之玩转字符串(1)
2014/09/14 Python
Python中Continue语句的用法的举例详解
2015/05/14 Python
使用python list 查找所有匹配元素的位置实例
2019/06/11 Python
python文字转语音的实例代码分析
2019/11/12 Python
使用python求解二次规划的问题
2020/02/29 Python
使用PyCharm安装pytest及requests的问题
2020/07/31 Python
详解CSS3弹性伸缩盒
2020/09/21 HTML / CSS
数据员岗位职责
2013/11/19 职场文书
顶岗实习计划书
2014/01/10 职场文书
圣诞节红领巾广播稿
2014/02/03 职场文书
公益广告宣传方案
2014/02/28 职场文书
2014爱耳日宣传教育活动总结
2014/03/09 职场文书
五好家庭事迹材料
2014/12/20 职场文书
《没有任何借口》读后感:完美的执行能力
2020/01/07 职场文书
Python批量将csv文件转化成xml文件的实例
2021/05/10 Python