Monthly Archives: October 2008

Cross-site data retrieval using JSONP

Traditional data retrieval (in JSON, XML or whatever) using XMLHttpRequest is limited to requests on the same domain. A way to work around this limitation is to use JSONP. The ‘P’ stands for Padding.

The main concept is to dynamically create a script tag and set its source attribute to the URL of the service which you would like your script to communicate with. The service responds with the relevant data in JSON format, but wraps it in a function call (that’s the padding!). The browser will evaluate the JavaScript immediately it is loaded and execute the function call with the JSON retrieved data as a regular object as it’s argument.

If the backend service takes the function name used to wrap the JSON data as an argument in the query string it is possible for the initiating script to set up a callback of choice.

If the backend service is called (i.e. setting the script src attribute) using the following URL:

http://www.stpe.se/service?callback=myMethod

The response from the backend service may look something like:

myMethod({firstValue: 123, secondValue: 456});

The only difference from a traditional JSON service is the padding with the function call (hence the P in JSONP). Of course additional parameters to the backend service may be appended as arguments or part of the URL in a RESTful way.

Here is an example of a JavaScript function that initiates the callback by creating a script tag:

function initJSONP(url) {
    // create script tag
    var script = document.createElement("script");
    script.setAttribute("type", "text/javascript");

    // append timestamp to avoid browser caching
    url += (url.indexOf("?") == -1 ? "?" : "&") + (new Date().getTime());

    // set script source to the service that responds with thepadded JSON data
    script.setAttribute("src", url);

    // insert script tag
    document.body.appendChild(script);

    // remove script tag by timeout (remove responsibility from callback)
    setTimeout(function() {
        document.body.removeChild(script);
    }, 2000);
}

Given our previous example it would be called using:

initJSONP("http://www.stpe.se/service?callback=myMethod");

If the same URL is called multiple times it may be a good idea to append something relatively unique (timestamp in this example) to the URL to avoid having the browser cache the call.

The other peculiarity is how the just created script tag is removed from the document. In this example I’m using a timeout of 2 seconds. The advantage is that the removal is done automatically and there is no responsibility of the callback function to clean up the document (which makes sense since the callback function may be a regular function also invoked from the main JavaScript itself). The disadvantage is if the service takes more than 2 seconds to respond, then the callback will never be made. Your milage may vary, so apply the method that suits your particular demands.

That’s all there is. 🙂

Javascript Array.indexOf() and Array.remove()

Useful convenience methods for array manipulation in Javascript.

Array.indexOf()

Find index of given element in array. This method is implemented in some browsers, but not all.

if (!Array.prototype.indexOf)
{
    /**
     * Add array.indexOf() functionality (exists in >FF 1.5 but not in IE)
     *
     * @param {Object} elem Element to find.
     * @param {Number} [from] Position in array to look from.
     */    
    Array.prototype.indexOf = function(elem /*, from*/) {
        var len = this.length;
        
        var from = Number(arguments[1]) || 0;
        from = (from < 0) ? Math.ceil(from) : Math.floor(from);
        if (from < 0) {
            from += len;
        }
        
        for (; from < len; from++) {
            if (from in this && this&#91;from&#93; === elem) {
                return from;
            }
        }
        
        return -1;
    };
}
&#91;/sourcecode&#93;

<h3>Array.remove()</h3>
Remove element in array (depends on indexOf method).


if (!Array.prototype.remove)
{
    /**
     * Add array.remove() convenience method to remove element from array.
     * 
     * @param {Object} elem Element to remove.
     */
    Array.prototype.remove = function(elem) {
        var index = this.indexOf(elem);
        
        if (index !== -1) {
            this.splice(index, 1);
        }        
    };
}

Simply add these two your Javascript file augment the functionality with the existing Array implementation.