分享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 相关文章推荐
jquery下拉select控件操作方法分享(jquery操作select)
Mar 25 Javascript
node.js中的buffer.length方法使用说明
Dec 14 Javascript
JavaScript动态改变div属性的实现方法
Jul 22 Javascript
jQuery+css实现的切换图片功能代码
Jan 27 Javascript
javascript解决小数的加减乘除精度丢失的方案
May 31 Javascript
可输入文字查找ajax下拉框控件 ComBox的实现方法
Oct 25 Javascript
原生node.js案例--前后台交互
Feb 20 Javascript
jsonp跨域请求详解
Jul 13 Javascript
AngularJs 终极购物车(实例讲解)
Nov 08 Javascript
详解Vue+axios+Node+express实现文件上传(用户头像上传)
Aug 10 Javascript
js实现简单点赞操作
Mar 17 Javascript
基于vue中的scoped坑点解说
Sep 04 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
从网上搜到的phpwind 0day的代码
2006/12/07 PHP
php生成随机字符串可指定纯数字、纯字母或者混合的
2014/04/18 PHP
php实现高效获取图片尺寸的方法
2014/12/12 PHP
PHP 极验验证码实例讲解
2016/09/29 PHP
Javascript 获取滚动条位置等信息的函数
2009/09/08 Javascript
js DOM的学习笔记
2011/12/22 Javascript
js bind 函数 使用闭包保存执行上下文
2011/12/26 Javascript
jquery方法+js一般方法+js面向对象方法实现拖拽效果
2012/08/30 Javascript
js实现touch移动触屏滑动事件
2015/04/17 Javascript
解决html-jquery/js引用外部图片时遇到看不了或出现403的问题
2017/09/22 jQuery
JS实现获取汉字首字母拼音、全拼音及混拼音的方法
2017/11/14 Javascript
Angular实现的简单定时器功能示例
2017/12/28 Javascript
layer弹出层倒计时关闭的实现方法
2019/09/27 Javascript
Vue实现 点击显示再点击隐藏效果(点击页面空白区域也隐藏效果)
2020/01/16 Javascript
Python 数据处理库 pandas 入门教程基本操作
2018/04/19 Python
关于不懂Chromedriver如何配置环境变量问题解决方法
2019/06/12 Python
用Python识别人脸,人种等各种信息
2019/07/15 Python
解决Django加载静态资源失败的问题
2019/07/28 Python
Pytorch在NLP中的简单应用详解
2020/01/08 Python
Keras使用tensorboard显示训练过程的实例
2020/02/15 Python
python模拟点击网页按钮实现方法
2020/02/25 Python
Window版下在Jupyter中编写TensorFlow的环境搭建
2020/04/10 Python
基于python实现ROC曲线绘制广场解析
2020/06/28 Python
css3的图形3d翻转效果应用示例
2014/04/08 HTML / CSS
DKNY品牌官网:纽约大都会时尚风格
2016/10/20 全球购物
加拿大在线眼镜零售商:SmartBuyGlasses加拿大
2019/05/25 全球购物
adidas泰国官网:adidas TH
2020/07/11 全球购物
C#如何允许一个类被继承但是避免这个类的方法被重载?
2015/02/24 面试题
sort命令的作用和用法
2013/08/25 面试题
实习单位接收函
2014/01/11 职场文书
企业口号大全
2014/06/12 职场文书
2014年学前班工作总结
2014/12/08 职场文书
2015年母亲节寄语
2015/03/23 职场文书
PyCharm 安装与使用配置教程(windows,mac通用)
2021/05/12 Python
PostgreSQL数据库创建并使用视图以及子查询
2022/04/11 PostgreSQL
Mysql 如何合理地统计一个数据库里的所有表的数据量
2022/04/18 MySQL