Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recreating jQuery's ajaxStart and ajaxComplete functionality

I'm trying to reproduce jQuery's functions ajaxComplete and ajaxStart without jQuery so that they could be used in any environment with no library dependencies (it's a special use case). These functions allow for an event listener to be called before and after any ajax request. In my example, I call them preAjaxListener and postAjaxListener.

I'm trying to accomplish it by hooking into the XMLHttpRequest object and overwriting/decorating open and send. Yes, I know this is dirty.

XMLHttpRequest.prototype.open = (function(orig){
    return function(a,b,c){
        this._HREF = b; // store target url
        return orig.apply(this, arguments); // call original 'open' function
    };
})(XMLHttpRequest.prototype.open);

XMLHttpRequest.prototype.send = (function(orig){
    return function(){
        var xhr = this;
        _core._fireAjaxEvents('pre', xhr._HREF); // preAjaxListener fires

        var rsc = xhr.onreadystatechange || function(){}; // store the original onreadystatechange if it exists
        xhr.onreadystatechange = function(){ // overwrite with custom function
            try {
                if (xhr.readyState == 4){
                    _core._fireAjaxEvents('post', xhr._HREF); // postAjaxListneer should fire
                    this.onreadystatechange = rsc;
                } 
            } catch (e){ }
            return rsc.apply(this, arguments); // call original readystatechange function
        };

        return orig.apply(this, arguments); // call original 'send' function
    };
})(XMLHttpRequest.prototype.send);

I do not want to write wrapper functions to make ajax requests. I want to be able to hook into any ajax request made by any library (or with vanilla js) on the page.

So far, only the preAjaxListener function works. I can't seem to figure out why, but it seems that onreadystatechange is never being called. Any guidance would be greatly appreciated.

Working demo: http://jsfiddle.net/_nderscore/QTQ5s/

like image 610
nderscore Avatar asked Dec 19 '13 18:12

nderscore


1 Answers

Using .onreadystatechange wasn't working because I was testing with jQuery and jQuery's ajax methods manipulate and removes the onreadystatechange property.

However, adding an event listener for loadend works just fine everywhere but IE. For IE, I set up an interval instead - not the optimal solution, but it works for my needs. I only intended this script to work on IE8+ and modern browsers.

XMLHttpRequest.prototype.send = (function(orig){
    return function(){
        _core._fireAjaxEvents('pre', this._HREF);

        if (!/MSIE/.test(navigator.userAgent)){
            this.addEventListener("loadend", function(){
                _core._fireAjaxEvents('post', this._HREF);
            }, false);
        } else {
            var xhr = this,
            waiter = setInterval(function(){
                if(xhr.readyState && xhr.readyState == 4){
                    _core._fireAjaxEvents('post', xhr._HREF);
                    clearInterval(waiter);
                }
            }, 50);
        }

        return orig.apply(this, arguments);
    };
})(XMLHttpRequest.prototype.send);
like image 186
nderscore Avatar answered Sep 25 '22 04:09

nderscore