Function Declarations



In fact, using asynchronous objects isn't the only way to send asynchronous requests. Using iframe is an example; creating an img element and setting the src property makes browsers request the image; creating a script element and setting the src property is also workable.

You can dynamically create a script element, set the src property, and append it to the DOM tree, so that the browser will download the specified script for you. For example: 
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'some.js';
document.getElementsByTagName('head')[0].appendChild(script);

The above code has the same effect as writing the follow markup in the head section in the page:
<script type="text/javascript" src="some.js"></script>

That's to say, browsers will execute the downloaded some.js. This is also a workable way to load JavaScript dynamically, and the basis of JSONP.

As we've seen in Security, JavaScript doesn't allow getting data from different origin. Asynchronous objects also need to comply with the same origin policy. It's common, however, to request data from a server in a different domain. If there's no way to do this, many sites can't provide their cross-site services.

The way to against this odd is using the way we've seen previously about downloading script dynamically. For example, if the current page has defined the following function:
function doSomething(json) {
    ...
}

If you download script dynamically as mentioned previously, and the downloaded code includes the following:
doSomething({"name":"Justin","age":35});

Then, browsers will use {"name": "Justin", "age": 35} - an object literal - to call the doSomething function. This is the basis of JSONP. JSONP stands for "JSON with padding". That is, JSON data with a prefix padding. The padding is typically the name of a callback function.

If the server's acceptable request parameters are as follows:
id=E123456&jsoncallback=handler

The padding - the name of the callback function - is decided by a request parameter. If the request parameter is jsoncallback=handler, the server returns the following:
handler({"name":"Justin","age":35});

Then, the client has more flexibility to decide which callback function is used to process the returned JSON. The following is a realistic example. You can open this page in the localhost and type an id to retrieve data.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">
<html>
    <head>
        <meta content="text/html; charset=UTF-8" http-equiv="content-type">
        <script type="text/javascript" src="js/json2.js"></script>
        <script type="text/javascript">
            window.onload = function() {
                function param(obj) {
                    var pairs = [];
                    for(var name in obj) {
                        var pair = encodeURIComponent(name) + '=' + 
                                   encodeURIComponent(obj[name]);
                        pairs.push(pair.replace('/%20/g', '+'));
                    }
                    return pairs.join('&');
                }
                
                function getScript(url, callback) {
                    var script = document.createElement('script');
                    script.type = 'text/javascript';
                    script.src = url;
                    
                    // Cross-browser processing events when download script is completed
                    script.onload = script.onreadystatechange = function() {
                        if (!this.readyState ||
                            this.readyState === "loaded" || 
                            this.readyState === "complete") {
                            this.onload = this.onreadystatechange = null;
                            document.getElementsByTagName('head')[0]
                                    .removeChild(this);
                            callback();
                        }
                    };
                    
                    document.getElementsByTagName('head')[0]
                            .appendChild(script);
                }
                
                function jsonp(option, callbackName) {
                    if(!option.url || !callbackName) {
                        return;
                    }
                    var data = option.data || {};
                    
                    // create a temporary function
                    data[callbackName] = 'XD' + jsonp.jsc++;
                    window[data[callbackName]] = function(json) {
                        option.callback(json);
                    };
                    var url = option.url + '?' + param(data);
                    
                    // download the script
                    getScript(url, function() {
                         // remove the temporary function 
                         // when the script is downloaded and executed
                         window[data[callbackName]] = undefined;
                         try {
                             delete window[data[callbackName]];
                         }
                         catch(e) {}
                    });
                }
                jsonp.jsc = new Date().getTime();
                
                document.getElementById('test').onclick = function() {
                    jsonp({
                        url      : 'https://openhome.cc/eGossip/' +
                                   'JavaScript/samples/JSONP-1.php',
                        data     : {
                            id   : document.getElementById('id').value,
                        },
                        callback : function(person) {
                            document.getElementById('result').innerHTML = 
                                person.name + ',' + person.age;
                        }
                    }, 'jsoncallback');
                };
            };
        </script>        
    </head>
    <body>
        ID:<input id="id">
        <button id="test">Test JSONP</button>
        <span id="result"></span>
    </body>
</html>

The URL http://api.flickr.com/services/feeds/photos_public.gne provides a service through JSONP. You can use the jsoncallback parameter to specify a callback function. The following example uses a tag to search images on Filick, and display them in the page.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">
<html>
    <head>
        <meta content="text/html; charset=UTF-8" http-equiv="content-type">
        <script type="text/javascript" src="js/json2.js"></script>
        <script type="text/javascript">
            window.onload = function() {
                function param(obj) {
                    var pairs = [];
                    for(var name in obj) {
                        var pair = encodeURIComponent(name) + '=' + 
                                   encodeURIComponent(obj[name]);
                        pairs.push(pair.replace('/%20/g', '+'));
                    }
                    return pairs.join('&');
                }
                
                function getScript(url, callback) {
                    var script = document.createElement('script');
                    script.type = 'text/javascript';
                    script.src = url;
                    
                    script.onload = script.onreadystatechange = function() {
                        if (!this.readyState ||
                            this.readyState === "loaded" || 
                            this.readyState === "complete") {
                            this.onload = this.onreadystatechange = null;
                            document.getElementsByTagName('head')[0]
                                    .removeChild(this);
                            callback();
                        }
                    };
                    
                    document.getElementsByTagName('head')[0]
                            .appendChild(script);
                }
                
                function jsonp(option, callbackName) {
                    if(!option.url || !callbackName) {
                        return;
                    }
                    var data = option.data || {};
                    
                    data[callbackName] = 'XD' + jsonp.jsc++;
                    window[data[callbackName]] = function(json) {
                        option.callback(json);
                    };
                    var url = option.url + '?' + param(data);
                    
                    getScript(url, function() {
                         window[data[callbackName]] = undefined;
                         try {
                             delete window[data[callbackName]];
                         }
                         catch(e) {}
                    });
                }
                jsonp.jsc = new Date().getTime();
                
                document.getElementById('search').onclick = function() {
                    jsonp({
                        url      : 'http://api.flickr.com/services/' + 
                                   'feeds/photos_public.gne',
                        data     : {
                            tagmode : 'any',
                            format  : 'json',
                            tags    : document.getElementById('tags').value
                        },
                        callback : function(data) {
                            var images = document.getElementById('images');
                            // empty all images
                            var length = images.childNodes.length;
                            for(var i = 0; i < length; i++) {
                                images.removeChild(images.firstChild);
                            }
                            // Flick retusn JSNOP. You can observe its data format.
                            // items is an array. Every element has a media property.
                            // the m property of media is the image's url.
                            var items = data.items;
                            for(var i = 0; i < items.length; i++) {
                                var img = document.createElement('img');
                                img.src = items[i].media.m;
                                images.appendChild(img);
                            }
                        }
                    }, 'jsoncallback');
                };
            };
        </script>        
    </head>
    <body>
    	<input id="tags"><br>
		<button id="search">Search</button>
    	<div id="images"></div>
    </body>
</html>