最近博主在做一个小项目,引入了第三方 js 文件,这个文件会调用 XMLHttpRequest
向服务器发送 Ajax 请求,但是我有需要监听其 Ajax 请求的某些事件,以便额外地执行其他脚本。于是,稍微看了看监听 Ajax 请求的事件方法,在这里分享给大家。
若 Ajax 请求是由 jQuery 的 $.ajax
发起的,默认情况下可以使用 jQuery 的 Global Ajax Event Handlers
监听到 Ajax 事件,然而我遇到的却是用原生 JavaScript 发起的 Ajax 请求,所以这种方法行不通。
然后呢,还有其他方法,比如说 Pub/Sub
,也就是 https://github.com/cowboy/jquery-tiny-pubsub 这个 jQuery 插件,但是这个发起请求的 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
返回的 e
是 XMLHttpRequest 对象
,但是自定义方法 ajaxReadyStateChange
等事件 handler
返回的 e
是 CustomEvent 对象
,而 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+ 上愉快地使用了,caniuse。
原创文章,转载请以链接形式注明出处:https://blog.ttionya.com/article-1511.html