分享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在IE中“意外地调用了方法或属性访问”
Nov 19 Javascript
jquery关于页面焦点的定位(文本框获取焦点时改变样式 )
Sep 10 Javascript
JavaScript制作的可折叠弹出式菜单示例
Apr 04 Javascript
jquery让指定的元素闪烁显示的方法
Mar 17 Javascript
js实现点击按钮后给Div图层设置随机背景颜色的方法
May 06 Javascript
Windows下用PyCharm和Visual Studio开始Python编程
Oct 26 Javascript
mui框架移动开发初体验详解
Oct 11 Javascript
vue滚动轴插件better-scroll使用详解
Oct 17 Javascript
jQuery获取所有父级元素及同级元素及子元素的方法(推荐)
Jan 21 jQuery
微信小程序实现倒计时补零功能
Jul 09 Javascript
基于axios 的responseType类型的设置方法
Oct 29 Javascript
Vue实现可移动水平时间轴
Jun 29 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
点评山进PR-D3L三波段收音机
2021/03/02 无线电
PHP在字符断点处截断文字的实现代码
2011/04/21 PHP
Drupal7中常用的数据库操作实例
2014/03/02 PHP
iOS自定义提示弹出框实现类似UIAlertView的效果
2016/11/16 PHP
PHP7 参数处理机制修改
2021/03/09 PHP
25个优雅的jQuery Tooltip插件推荐
2011/05/25 Javascript
JS正则表达式验证数字代码
2014/01/28 Javascript
js 实现省市区三级联动菜单效果
2017/02/20 Javascript
webpack配置打包后图片路径出错的解决
2018/04/26 Javascript
vue里input根据value改变背景色的实例
2018/09/29 Javascript
vue+element模态框中新增模态框和删除功能
2019/06/11 Javascript
jQuery实现简单弹幕效果
2019/11/28 jQuery
element-ui 远程搜索组件el-select在项目中组件化的实现代码
2019/12/04 Javascript
使用vue实现一个电子签名组件的示例代码
2020/01/06 Javascript
解决vue scoped scss 无效的问题
2020/09/04 Javascript
Python进行数据科学工作的简单入门教程
2015/04/01 Python
Python自定义scrapy中间模块避免重复采集的方法
2015/04/07 Python
使用Django的模版来配合字符串翻译工作
2015/07/27 Python
利用Python命令行传递实例化对象的方法
2016/11/02 Python
使用Python实现windows下的抓包与解析
2018/01/15 Python
Python使用SQLite和Excel操作进行数据分析
2018/01/20 Python
Python实现购物评论文本情感分析操作【基于中文文本挖掘库snownlp】
2018/08/07 Python
python实现PID算法及测试的例子
2019/08/08 Python
python抓取多种类型的页面方法实例
2019/11/20 Python
python实现跨excel sheet复制代码实例
2020/03/03 Python
基于virtualenv创建python虚拟环境过程图解
2020/03/30 Python
详解python命令提示符窗口下如何运行python脚本
2020/09/11 Python
编辑找工作求职信分享
2014/01/03 职场文书
办公室主任主任岗位责任制
2014/02/11 职场文书
学生安全承诺书
2014/05/22 职场文书
高一课前三分钟演讲稿
2014/09/13 职场文书
2014光棍节大学生联谊活动方案
2014/10/10 职场文书
《合作意向书》怎么写?
2019/08/20 职场文书
nginx优化的六点方法
2021/03/31 Servers
SQL Server作业失败:无法确定所有者是否有服务器访问权限的解决方法
2021/06/30 SQL Server
GTX1650super好不好 gtx1650super显卡属于什么级别
2022/04/08 数码科技