分享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 ajax 路由和过滤器使用说明
Aug 02 Javascript
Javascript 浮点运算的问题分析与解决方法
Aug 27 Javascript
js与jQuery实现checkbox复选框全选/全不选的方法
Jan 05 Javascript
浅谈jquery点击label触发2次的问题
Jun 12 Javascript
Bootstrap模态框水平垂直居中与增加拖拽功能
Nov 09 Javascript
微信小程序之拖拽排序(代码分享)
Jan 21 Javascript
解决OneThink中无法异步提交kindeditor文本框中修改后的内容方法
May 05 Javascript
在一个页面实现两个zTree联动的方法
Dec 20 Javascript
vue中使用element ui的弹窗与echarts之间的问题详解
Oct 25 Javascript
Vue + element 实现多选框组并保存已选id集合的示例代码
Jun 03 Javascript
在Vue中使用HOC模式的实现
Aug 23 Javascript
在vue中使用cookie记住用户上次选择的实例(本次例子中为下拉框)
Sep 11 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入门之常量简介和系统常量
2014/05/12 PHP
php学习笔记之字符串常见操作总结
2019/07/16 PHP
jquery easyui的tabs使用时的问题
2010/03/23 Javascript
Jquery性能优化详解
2014/05/15 Javascript
javascript递归回溯法解八皇后问题
2015/04/22 Javascript
跟我学习javascript的函数调用和构造函数调用
2015/11/16 Javascript
node.js缺少mysql模块运行报错的解决方法
2016/11/13 Javascript
微信小程序js文件改变参数并在视图上及时更新【推荐】
2018/06/11 Javascript
详解vue-cli3使用
2018/08/14 Javascript
微信小程序中this.data与this.setData的区别详解
2018/09/17 Javascript
iview同时验证多个表单问题总结
2018/09/29 Javascript
JS高阶函数原理与用法实例分析
2019/01/15 Javascript
jquery实现二级导航下拉菜单效果实例
2019/05/14 jQuery
Python实现数据库编程方法详解
2015/06/09 Python
python将字典内容存入mysql实例代码
2018/01/18 Python
python操作文件的参数整理
2019/06/11 Python
python+numpy按行求一个二维数组的最大值方法
2019/07/09 Python
Python3 xml.etree.ElementTree支持的XPath语法详解
2020/03/06 Python
六种酷炫Python运行进度条效果的实现代码
2020/07/17 Python
python爬虫线程池案例详解(梨视频短视频爬取)
2021/02/20 Python
捷克建筑材料网上商店:DEK.cz
2021/03/06 全球购物
北京天润融通.net面试题笔试题
2012/02/20 面试题
航空大学应届生求职信
2013/11/10 职场文书
廉政教育心得体会
2014/01/01 职场文书
春节活动策划方案
2014/01/24 职场文书
学习雷锋做美德少年寄语大全
2014/04/09 职场文书
《雕塑之美》教学反思
2014/04/24 职场文书
兽医医药专业求职信
2014/07/27 职场文书
总经理岗位职责说明书
2014/07/30 职场文书
社区助残日活动总结
2014/08/29 职场文书
公司感恩节活动策划书
2014/10/11 职场文书
会计工作总结范文2014
2014/12/23 职场文书
小学工作总结2015
2015/05/04 职场文书
先进教师个人主要事迹材料
2015/11/03 职场文书
python3.7.2 tkinter entry框限定输入数字的操作
2021/05/22 Python
python内置模块之上下文管理contextlib
2022/06/14 Python