JavaScript: implementing getJSON() from scratch

JavaScript: implementing getJSON() from scratch

How to implement from scratch a JavaScript method to fetch JSON and JSONP data.

Why we should try to implement a getJSON() method from scratch when jQuery already has a complete method on its own? Because we're not lazy developers and we want to try to do our best in order to accomplish something that is not so trivial as it could seem. And yes, we want to support also JSONP.

The first obvious thing that comes to our attention is the fact that we need the XMLHttpRequest object. This object is the basic foundation of every AJAX request. Today's browsers support this object natively (mainly because it was finally standardized by the W3C):


(function() {
    var Lib = {
        ajax: {
            xhr: function() {
                var instance = new XMLHttpRequest();
                return instance;
            },
            // code continues
        }
    };
    
    window.Lib = Lib;
})()

Then we need a URL to retrieve our AJAX response and a callback function to parse the response. In the case of a JSON response, the responseText property is treated by browsers as a JavaScript object (obviously if the JSON string was properly created by the server-side script).

The URL for our example will be as follows:

https://api.twitter.com/1/statuses/user_timeline.json?&screen_name=gabromanato&callback=?&count=1

Yes, I want to retrieve my latest tweet from Twitter. But there's a catch: due to the fact that the remote resource is located on a different server, browsers will apply the standard restrictions of the same-domain policy.

In this case, however, the URL parameter &callback=? indicates that the remote resource is using JSONP (JSON with Padding). In other words we have to:

  1. replace the ? sign with the name of a function used to parse the response
  2. embed the remote JSON file within our document by creating a custom script element.

Our code, however, creates a sandbox around the Lib object and its members. In this case we can't specify the name of a method as our JSONP callback because this method is not visible outside its scope. For that reason, we'll create an alias and bind the newly created alias to the window object:


// code continues

getJSON: function(options, callback) {
                var xhttp = this.xhr();
                options.url = options.url || location.href;
                options.data = options.data || null;
                callback = callback ||
                function() {};
                options.type = options.type || 'json';
                var url = options.url;
                
                if (options.type == 'jsonp') { // JSONP
                    window.jsonCallback = callback; // Now our callback method is globally visible
                    var $url = url.replace('callback=?', 'callback=jsonCallback');
                    var script = document.createElement('script');
                    script.src = $url;
                    document.body.appendChild(script);
                }
                
                
                xhttp.open('GET', options.url, true);
                xhttp.send(options.data);
                xhttp.onreadystatechange = function() {
                    if (xhttp.status == 200 && xhttp.readyState == 4) {
                        callback(xhttp.responseText);
                    }
                };
}

A simple test:


Lib.ajax.getJSON({
        url: 'https://api.twitter.com/1/statuses/user_timeline.json?&screen_name=gabromanato&callback=?&count=1',
        type: 'jsonp'
    }, function(tweet) {
        document.querySelector('#tweet').innerHTML = tweet[0].text;
});

You can see the demo on this page.