分享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 相关文章推荐
IE7提供XMLHttpRequest对象为兼容
Mar 08 Javascript
javascript删除html标签函数cIsHTML
Jan 09 Javascript
js 将canvas生成图片保存,或直接保存一张图片的实现方法
Jan 02 Javascript
原生JS实现多个小球碰撞反弹效果示例
Jan 31 Javascript
基于IView中on-change属性的使用详解
Mar 15 Javascript
Angular 容器部署的方法
Apr 17 Javascript
通过cordova将vue项目打包为webapp的方法
Feb 02 Javascript
对layui数据表格动态cols(字段)动态变化详解
Oct 25 Javascript
vue 获取及修改store.js里的公共变量实例
Nov 06 Javascript
JavaScript实现简易聊天对话框(加滚动条)
Feb 10 Javascript
Vue向后台传数组数据,springboot接收vue传的数组数据实例
Nov 12 Javascript
Javascript设计模式之原型模式详细
Oct 05 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生成静态文件的多种方法分享
2012/07/17 PHP
PHP与MongoDB简介|安全|M+PHP应用实例详解
2013/06/17 PHP
php中Array2xml类实现数组转化成XML实例
2014/12/08 PHP
php+mysqli实现将数据库中一张表信息打印到表格里的方法
2015/01/28 PHP
Codeigniter校验ip地址的方法
2015/03/21 PHP
PHP简单遍历对象示例
2016/09/28 PHP
WHOOPS PHP调试库的使用
2017/09/29 PHP
向fckeditor编辑器插入指定代码的方法
2007/05/25 Javascript
jQuery 添加/移除CSS类实现代码
2010/02/11 Javascript
jquery的extend和fn.extend的使用说明
2011/01/09 Javascript
javascript 三种方法实现获得和设置以及移除元素属性
2013/03/20 Javascript
js 为label标签和div标签赋值的方法
2013/08/08 Javascript
调试代码导致IE出错的避免方法
2014/04/04 Javascript
JS实现动态给图片添加边框的方法
2015/04/01 Javascript
jQuery 常用代码集锦(必看篇)
2016/05/16 Javascript
ionic进入多级目录后隐藏底部导航栏(tabs)的完美解决方案
2016/11/23 Javascript
jquery实现图片平滑滚动详解
2017/03/22 jQuery
浅谈Angular文字折叠展开组件的原理分析
2017/11/24 Javascript
JQuery选中select组件被选中的值方法
2018/03/08 jQuery
jQuery实现动态添加和删除input框代码实例
2019/03/29 jQuery
ES6 Promise对象的应用实例分析
2019/06/27 Javascript
react quill中图片上传由默认转成base64改成上传到服务器的方法
2019/10/30 Javascript
javascript实现简易计算器功能
2020/09/23 Javascript
vue中可编辑树状表格的实现代码
2020/10/31 Javascript
Python中operator模块的操作符使用示例总结
2016/06/28 Python
Python 中pandas.read_excel详细介绍
2017/06/23 Python
Python中enumerate()函数编写更Pythonic的循环
2018/03/06 Python
selenium+python 去除启动的黑色cmd窗口方法
2018/05/22 Python
python 合并多个excel中同名的sheet
2021/01/22 Python
CSS3不透明度实例讲解
2016/04/26 HTML / CSS
墨西哥运动服饰和鞋网上商店:Netshoes墨西哥
2016/07/28 全球购物
解释一下ruby中的特殊方法与特殊类
2013/02/26 面试题
大学生毕业的自我鉴定
2013/11/13 职场文书
医学类导师推荐信范文
2013/11/19 职场文书
紧急迫降观后感
2015/06/15 职场文书
Echarts如何重新渲染实例详解
2022/05/30 Javascript