若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返回的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+ 上愉快地使用了,以上就是本文的全部內容,希望大家能夠喜歡。