Lightweight JSONP javascript library


This can now be found at github: https://github.com/IntoMethod/Lightweight-JSONP

Are you one of those that include jQuery or any other library only to be able to use some JSONP in your website? In that case this might be the code for you to read and try to understand. I won’t post any detailed explainations right now but feel free to comment if you wonder anything. I’ve also included a minified version in the bottom.

Update (2012-04-10) Fixed issues/suggestions found by Zach, Gabriel and Pamela (see comments). These updates and fixes can be found on github

Update (2011-06-08) Fixed delete/setting to null as pointed out by Bryan Smith (see comment)

Update (2011-04-29) Added encodeURIComponent as suggested by yched (see comment)

Update: Fixed scoping problem as found by Damon Oehlman (see comment)

Feel free to use this script for anything that you like, commercial or non-commercial, but please keep the comment and link to this site.

Oh and by the way, the script has been tested successfully in the following browsers:
Chrome 4,5,6
Firefox 3.x
Internet Explorer 6,7,8
Opera 9.6+10
Androids webkit-based browser
Symbian S60 browser

First an example of how to use it, to show the simplicity, then the actual code.

JSONP.get( 'someUrl.php', {param1:'123', param2:'456'}, function(data){
        //do something with data, which is the JSON object you output from someUrl.php
        //into the callback javascript method specified by the "callback" value in the querystring
});

A simple example of the someUrl.php (please observe this example does not include any security checks what so ever, just to show the principle):

$myData = fetchSomeData();
 
echo $_GET['callback'];
echo "(";
echo json_encode($myData);
echo ")";

See github link above for current code and examples, the code below is old

The actual code for the Lightweight JSONP functionality.
Around 1.1kb without gzip.

//Lightweight JSONP fetcher - www.nonobtrusive.com
var JSONP = (function(){
	var counter = 0, head, query, key, window = this;
	function load(url) {
		var script = document.createElement('script'),
			done = false;
		script.src = url;
		script.async = true;
 
		script.onload = script.onreadystatechange = function() {
			if ( !done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") ) {
				done = true;
				script.onload = script.onreadystatechange = null;
				if ( script && script.parentNode ) {
					script.parentNode.removeChild( script );
				}
			}
		};
		if ( !head ) {
			head = document.getElementsByTagName('head')[0];
		}
		head.appendChild( script );
	}
	function jsonp(url, params, callback) {
		query = "?";
		params = params || {};
		for ( key in params ) {
			if ( params.hasOwnProperty(key) ) {
				query += encodeURIComponent(key) + "=" + encodeURIComponent(params[key]) + "&";
			}
		}
		var jsonp = "json" + (++counter);
		window[ jsonp ] = function(data){
			callback(data);
			try {
				delete window[ jsonp ];
			} catch (e) {}
			window[ jsonp ] = null;
		};
 
		load(url + query + "callback=" + jsonp);
		return jsonp;
	}
	return {
		get:jsonp
	};
}());

, ,

  1. #1 by Damon Oehlman on July 20, 2010 - 2:34 pm

    Fantastic work – exactly what I am looking for. I’ll have a look at integrating this into an application and some underlying javascript libraries that I am writing at the moment.

    Library is released under MIT-style licence and code is available on github:

    http://github.com/sidelab/grunt

    I’ll make sure appropriate links back to your site are included in the source if I include it.

    Cheers,
    Damon.

  2. #2 by Erik Karlsson on July 23, 2010 - 10:28 am

    Hi and thanks for your comment. I’ve applied the fix for the jsonp scoping problem you ran into and updated the post. Good luck with the rest of your library and I’m glad that you found this useful

  3. #3 by Andrew Lewin on July 30, 2010 - 10:31 pm

    Thanks for this great Lightweight JSONP javascript library. Exactly what I was looking for.

    Cheers

  4. #4 by Joe Gutierrez on August 7, 2010 - 12:58 am

    @Andrew – Could you specifically state what caused your scoping problems? How did you identify them? I can’t find any reference for that here.

  5. #5 by Joe Gutierrez on August 7, 2010 - 12:59 am

    OOPs! Not @Andrew, but @Erik. Sorry for that.

  6. #6 by Erik Karlsson on August 7, 2010 - 9:55 pm

    Hi Joe, the blogpost is updated but I made an example of the broken vs fixed (as seen in the post right now) versions. The thing is that the last call breaks when doing multiple calls quickly due to the jsonp variable behaves like a static in the first version. In the broken version below you’ll see a javascript error being thrown for “jsonp10 is not defined”. Please note that I’ve added a 1 second delay to the response on the server.

    http://nonobtrusive.com/wp-content/uploads/2010/jsonp/broken/

    http://nonobtrusive.com/wp-content/uploads/2010/jsonp/fixed/

  7. #7 by Erik Karlsson on August 7, 2010 - 10:00 pm

    I just updated the minified version in the blogpost to reflect the fixed version, just copy paste it!

  8. #8 by Pamela Fox on September 20, 2010 - 1:48 am

    Thanks for this!

    I think the sample code should be JSON.get, not JSON.fetch.

    Also, I had to add another param to it as I’m using a service that uses a different callback param name than “callback”, unfortunately. Was easy to mod though.

    Thanks again!

  9. #9 by Erik Karlsson on September 20, 2010 - 8:21 am

    Pamela, I’m glad you found this useful. I’ll correct the typo in the sample, thanks for pointing it out.

  10. #10 by yched on April 28, 2011 - 5:48 pm

    the components in the querystring should be encoded :

    query += encodeURIComponent(key) + “=” + encodeURIComponent(params[key]) + “&”;

    insetad of
    query += key + “=” + params[key] + “&”;

  11. #11 by Erik Karlsson on April 29, 2011 - 8:14 am

    @yched: of course they should be, you’re right. Thanks for your comment

  12. #12 by Erik Karlsson on April 29, 2011 - 8:17 am

    @ysched: I’ve now updated the post with encodeURIComponent.

  13. #13 by Slava Shkodin on May 5, 2011 - 6:01 pm

    Hi, Erik. I have one question about your code (sorry, if it is a little bit stupid). I can’t understand why do you use both onload and onreadystatechange events? Why not simply onload?

  14. #14 by Bryan Smith on June 4, 2011 - 9:31 am

    Thanks!

    I think the sample code should try to delete window[jsonp] before setting it to null.

  15. #15 by Erik Karlsson on June 8, 2011 - 8:04 am

    Of course it should. Thanks for pointing it out.

  16. #16 by Ralph Bunker on June 21, 2011 - 6:14 pm

    Thanks for the code. I feel that I fully understand the jsonp pattern now.

    But why are you setting window[jsonp] to null after deleting it? Flanagan’s book does not use that pattern.

  17. #17 by Gabriel on January 6, 2012 - 6:54 pm

    Hey, thank you very much for the code!

    I just wanted to let you know that I found an improvement you can make to it.

    When you say:

    query = “?”;

    Keep in mind that the request URL may already have a query string in it, and thus the “?” should be replaced with a “&”.

    What do you think?

  18. #18 by Zach on March 31, 2012 - 6:20 pm

    Erik, thanks for your great code!

    +1 @Pamela. I’m working with the Tumblr API, and instead of “callback=foo” it expects “jsonp=foo”.

    Erik, I modified your jsonp fn to accept an optional last param, callbackName. This is for when APIs expect another param name instead of “callback=”.

    // function jsonp(url, params, callback, callbackName) {

    // load(url + query + (callbackName || “callback”) + “=” + jsonp);

    Cheers!

  19. #19 by Erik Karlsson on April 10, 2012 - 8:23 am

    Thank you all for comments and suggestions. I’ll review the modifications and update the post + github code soon.

  20. #20 by Erik Karlsson on April 10, 2012 - 8:46 am

    Slava Shkodin :

    Hi, Erik. I have one question about your code (sorry, if it is a little bit stupid). I can’t understand why do you use both onload and onreadystatechange events? Why not simply onload?

    This is for older IE support.

  21. #21 by Erik Karlsson on April 10, 2012 - 8:51 am

    @Pamela, @Zach, @Gabriel I added all of your suggestions and fixes to github with some minor additions like setting the callback name with a configuration object instead of having to pass it every time, still overridable. See changelog at github and updated readme.

  22. #22 by Crissaegrim on June 21, 2012 - 4:15 pm

    Hi !

    Nice lil’ library !
    However, I wanted to point out a little lack : recursive object encoding.

    The object { test: { one: 1, two: 2 } } will be encoded as “[object Object]“.

    Thanks to http://stackoverflow.com/a/1714899, I put this fix :

    function build_query(obj, prefix) {
    var str = [];
    for(var p in obj) {
    var k = prefix ? prefix + “[" + p + "]” : p, v = obj[p];
    str.push(typeof v == “object” ?
    build_query(v, k) :
    encodeURIComponent(k) + “=” + encodeURIComponent(v));
    }
    return str.join(“&”);
    }

    function jsonp(url, params, callback, callbackName) {
    query = (url || ”).indexOf(‘?’) === -1 ? ‘?’ : ‘&’;
    params = params || {};
    query += build_query(params) + “&”;

    var jsonp = “json” + (++counter);
    window[jsonp] = function (data) {
    callback(data);
    try {
    delete window[jsonp];
    } catch (e) {}
    window[jsonp] = null;
    };
    load(url + query + (callbackName || config['callbackName'] || ‘callback’) + ‘=’ + jsonp);
    return jsonp;
    }

  23. #23 by Roberto Pineda on November 24, 2012 - 3:34 am

    excellent !! i was looking for a solution for blackberry 5 and works !

  24. #24 by posicionamiento web malaga on December 29, 2012 - 3:54 pm

    You need to be a part of a contest for one of the highest quality sites online.
    I most certainly will recommend this website!

  25. #25 by Magnus on January 15, 2013 - 1:40 am

    How can I handle a syntax error from the webservice.
    If the service returns a malformed response, neither the success or error handler seem to get called.

  26. #26 by Magnus on January 15, 2013 - 3:13 am

    I hacked in some functionality to call the error handler when no callback is triggered(IE: when there is a malformed response).

    It seems to work on the browsers i’ve tested, firefox 17, chrome 23 and IE9.

    jsonp = (function(){
    var counter = 0, head, window = this, config = {};
    function load(url, pfnError, callbackStatus) {
    var script = document.createElement(‘script’),
    done = false;
    script.src = url;
    script.async = true;

    var errorHandler = pfnError || config.error;
    if ( typeof errorHandler === ‘function’ ) {
    script.onerror = function(ex){
    errorHandler({url: url, event: ex});
    };
    }

    script.onload = script.onreadystatechange = function() {
    if ( !done && (!this.readyState || this.readyState === “loaded” || this.readyState === “complete”) ) {
    done = true;
    script.onload = script.onreadystatechange = null;
    if ( script && script.parentNode ) {
    script.parentNode.removeChild( script );
    }
    if(!callbackStatus.received){
    errorHandler({url: url, event:”Unreadable Response”});
    }
    }
    };

    if ( !head ) {
    head = document.getElementsByTagName(‘head’)[0];
    }
    head.appendChild( script );
    }
    function encode(str) {
    return encodeURIComponent(str);
    }
    function jsonp(url, params, callback, callbackName, errorHandler) {
    var query = (url||”).indexOf(‘?’) === -1 ? ‘?’ : ‘&’, key;

    callbackName = (callbackName||config['callbackName']||’callback’);
    var uniqueName = callbackName + “_json” + (++counter);

    params = params || {};
    for ( key in params ) {
    if ( params.hasOwnProperty(key) ) {
    query += encode(key) + “=” + encode(params[key]) + “&”;
    }
    }
    var callbackStatus = {received : false};
    window[ uniqueName ] = function(data){
    callbackStatus.received = true;
    callback(data);
    try {
    delete window[ uniqueName ];
    } catch (e) {}
    window[ uniqueName ] = null;
    };
    load(url + query + callbackName + ‘=’ + uniqueName, errorHandler, callbackStatus);
    return uniqueName;
    }
    function setDefaults(obj){
    config = obj;
    }
    return {
    get:jsonp,
    init:setDefaults
    };
    }());

  27. #27 by diebao88.com on April 18, 2013 - 6:55 pm

    The article features established helpful to us.
    It’s extremely informative and you’re naturally extremely educated in this area. You possess popped my own eye for you to various thoughts about this specific matter together with intriguing, notable and strong articles.

  28. #28 by Frank White on July 11, 2013 - 8:11 am

    Hi,

    How would I use need to modify the code in order to use long polling?

    Kind regards,
    Frank

  29. #29 by Winston on September 6, 2013 - 7:37 am

    Very descriptive post, I enjoyed that bit. Wiill there be a part 2?

  30. #30 by Freon on December 12, 2013 - 2:37 pm

    There is a problem with encoding multi level data this way. Here is a recursive function that I used to encode the data into the url:

    function toQueryString(params, parentName) {
    var query = ”, key, name, k;
    for (key in params) {
    if (params.hasOwnProperty(key)) {
    k = encodeURIComponent(key);
    name = parentName ? parentName + ‘[' + k + ']‘ : k;
    query += typeof params[key] == ‘object’
    ? toQueryString(params[key], name)
    : name + “=” + encodeURIComponent(params[key]) + “&”;
    }
    }
    return query;
    }

  31. #31 by abnormal vaginal discharge pictures on January 23, 2014 - 10:16 am

    I do not know if it’s just me or if everyone else experiencing issues with your blog.
    It appears like some of the text within your content are running
    off the screen. Can someone else please comment and let me know if this is happening to them too?

    This may be a issue with my browser because I’ve had this happen before.
    Many thanks

  32. #32 by alquiler de tiendas online on February 4, 2014 - 5:37 am

    Hello, i believe that i noticed you visited my web site thus i got here to go back the want?.I’m attempting to in finding things to improve my web site!I guess its ok to use a few of your ideas!!

  33. #33 by cbs.com on February 26, 2014 - 4:49 am

    ӏ think that what you pսblished was actually very logical.

    But, thinк on this, whɑt if you added a little information?

    I mean, I don’t wish to tell you hhow to run youг weƄsite,
    hoաever suppose you added a title tօ possiЬly get folk’ѕ attention?
    I meaո Lightwdight JSOΝP javascript library

  34. #34 by code on May 14, 2014 - 7:33 am

    I cherished as much as you will obtain carried out proper here. The comic strip is tasteful, your authored material stylish. nevertheless, you command get got an shakiness over that you wish be turning in the following. unwell undoubtedly come more until now again as exactly the same nearly a lot steadily inside of case you defend this increase.

  35. #35 by wrong pick up lines on June 10, 2014 - 3:26 pm

    You actually make it seem really easy along with your presentation but I find this matter to be really
    something that I feel I might never understand. It sort of
    feels too complicated and extremely wide for me.
    I’m taking a look ahead in your subsequent publish, I’ll attempt to
    get the grasp of it!

  36. #36 by bästa flygpris on June 20, 2014 - 6:52 pm

    Hurrah, that’s what I was exploring for, what a data!
    existing here at this blog, thanks admin of this website.

(will not be published)