How to access cross-domain data with AJAX using JSONP, jQuery and PHP

I’m guessing that if you are reading this post you have already encountered the problem of the same origin policy with regards to retrieving data with AJAX from urls in other domains than your’s (the requesting domain). This policy, enforced by browsers, means you cannot fetch raw data from other domains with straight AJAX calls.

The Problem I Was Trying To Solve (skip this paragraph if you just want the solution)
Here’s the situation I ran into. At work, we have a timesheet entry program running on one subdomain, and Flyspray (bug-tracking software) on another subdomain. We wanted to get task descriptions from Flyspray integrated in our employee timesheet notes. Basically, the employee types a Flyspray task # for a given task they are working on, anywhere in their timesheet log entries. A jQuery script then converts that task # into a link to the task. In addition, that same script lets the user just hover over the task # to see a summary description of the task. When they hover, jQuery fires an AJAX request to a PHP script on the Flyspray subdomain. I couldn’t get it to work, so after researching it on jQuery sites I discovered my need for JSONP.

JSONP Circumvents the Same Origin Policy
Data returned from an AJAX call in JSONP (JSON with padding) format is designed to solve this problem. In jQuery, you can specify that you want either JSONP or JSON data returned from your AJAX calls. The differences between the expected returned formats of JSONP and JSON data are as follows:

  • JSON returns a JSON-formatted object only. For example: { "id" : "mydomelementid", "message": "This data came from a server!"} . This is a JSON object with 2 properties, id and message, and their corresponding values. See json.org to find out more about JSON.
  • JSONP returns a JSON-formatted object wrapped inside a function call. For example: jsonp123( { "id" : "mydomelementid", "message": "This data came from a server!"}); .

JSONP works by injecting what looks like a local function call directly into your script, attaching it to a script tag (in the form of a url). Wikipedia has a great explanation of this.

jQuery makes it easy to do AJAX calls implementing JSONP. You can use the simple getJSON method, or the ajax method. I’ll show you examples using both. First, though, let’s look at some example PHP code, so you know how to code the server side stuff. This part is what was not clear to me when I started researching JSONP.

The Server-Side Code (PHP)
In our example, we want to return a JSON object with a property of “message” that contains some feedback text to show the user when they click a button.

<?php
header("content-type: application/json"); 

// Create a generic object.
// Assign it the properties of id (optional - id of target HTML element, from URL) 
// and 'message' - the feedback message we want the user to see. 
if (isset($_GET['id'])) $rtnjsonobj->id = $_GET['id']; 
$rtnjsonobj->message = "You got an AJAX response via JSONP from another site!";


// Wrap and write a JSON-formatted object with a function call, using the supplied value of parm 'callback' in the URL: 
echo $_GET['callback']. '('. json_encode($rtnjsonobj) . ')';    

?>

This script returns a string in JSON-formatted object notation, wrapped in a function call. The name of the function comes from the URL parameter ‘callback’. The JSON object has one expected property (“message”) and one optional one, “id”. You provide the “id” parameter value on the URL string, also. I’ll explain its use in the second jQuery example. So, let’s say we call our script jsonp.php. The url to call it could look like this: "http://www.example.com/jsonp.php?callback=jsonp123". What json.php writes back to the server will be: json123({"message": "You got an AJAX response via JSONP from another site!"}); . If this were a JSON call instead, we’d omit – json123(); – the function call wrapper.

(Note our PHP script creates an object on the fly, without declaring a class and instantiating one. Our object is an instance of the stdClass class, a generic PHP object. Just assigning a property to it: $rtnjsonobj->;message = "You got an AJAX response via JSONP from another site!"; achieves this. The json_encode function then converts the PHP object to a JSON-formatted string. )

The Client-Side Code (jQuery)
I’ll show you 2 examples using jQuery AJAX functions to get the message produced by jsonp.php. The first example uses the typical jQuery anonymous callback function programming pattern. The second uses a named callback function.

Example 1

$(document).ready(function() {
 $("#jsonpbtn").click(function() {
	var surl =  "http://www.fbloggs.com/sandbox/jsonp.php?callback=?"; 
	var me = $(this); 
	$.getJSON(surl,  function(rtndata) { 
		me.feedback(rtndata.message, {duration: 4000, above: true});
    }); 
 });
}); 

This example is the simplest implementation of JSONP with jQuery. It calls our PHP script jsonp.php, passing a single parameter on the URL – callback=?. This specific syntax is the key to this being considered a JSONP AJAX call, rather than a JSON call. Note that the method is still getJSON- not getJSONP. The callback=? convention tells jQuery to generate a random function name and pass that as the value of the parameter named callback. Recall that our PHP script gets this function name with $_GET[‘callback’] , so we don’t have to worry about the actual generated name. Then, the anonymous function call (the second option on the getJSON method) does the work. In this case, it references the jQuery object that was clicked (the variable me) and calls my feedback plugin to display the message text. The fragment rtndata.message means ‘get the message property of the object rtndata (the JSON object returned by our PHP script). Feedback simply displays the message above the clicked button, for 4 seconds.

Example 2
This example uses a named callback function. Note that this capability is only supported in jQuery 1.4, so make sure you have the latest and greatest installed. As of this post, I’m using 1.4.2. I’m not sure why you would want to use a named callback function, other than to conform to DRY. The jQuery docs mention obliquely that you might want to cache your AJAX responses, but they give no explanation of how or why. Anyway, I thought it was worth trying, so here it is:

$(document).ready(function() {
$("#jsonpbtn2").click(function() {
	var surl =  "http://www.fbloggs.com/sandbox/jsonp.php"; 
	var id = $(this).attr("id"); 
	$.ajax({
		url: surl, 
		data: {id: id},
		dataType: "jsonp",
		jsonp : "callback",
		jsonpCallback: "jsonpcallback"
		}); 
 });
}); 

// Named callback function from the ajax call when jsonpbtn2 clicked
function jsonpcallback(rtndata) { 

	// Get the id from the returned JSON string and use it to reference the target jQuery object.
	var myid = "#"+rtndata.id; 
	$(myid).feedback(rtndata.message, {duration: 4000, above: true});
}

In this case, we can’t use the shorthand getJSON method, because it doesn’t have enough options. Instead, we must use the longer form ajax method. No big deal.
Here’s what’s going on. First, note the URL does not contain the callback=? parameter. Instead, we use the options jsonp and jsonpCallback, which is like writing: "jsonp.php?callback=jsonpcallback" for our url. Next, we pass the id of the DOM element (button) we clicked, as another parameter on the URL. Recall that our PHP script looks for this, and if it finds it, sends it back as a property of the returned JSON object. Next, we specify the expected data type returned from our server-side script (JSONP).

The cycle of code execution for this example is:

  • User clicks button with id of jsonbtn2
  • The browser invokes the $.ajax() method
  • The AJAX call invokes the server-side script, jsonp.php
  • jsonp.php gets the button’s id from the url parameter, constructs a JSON object with id and message properties and writes it back to the browser
  • The browser responds by running function jsonpcallback. It expects one parameter- our returned JSON object.
  • jsonpcallback retrieves the clicked button’s id from the returned JSON object, constructs a valid jQuery selector with it, and calls the feedback plugin to display the message above the button, just like in the simpler anonymous function example.

You should now understand why we pass the id back and forth. We need it in order to keep track of which element we are acting upon. We didn’t need to do this in the first example because of the magic of closures, another big topic.

That’s it!

See The Examples At Work
I’ve posted this page to show you working examples. You can view source if you want to see the jQuery code. It should be identical to what I’ve posted.

Considerations When Using JSONP
JSONP relies on injecting a script tag into your page. A side effect of this is that if you have errors in the data returned by your server, you won’t see any errors- the script tag just fails silently. An example of an error might be an incorrect callback function name (only likely in scenarios using Example 2 coding pattern), or malformed JSON.

When I first tried coding this, I got a Firebug error of ‘Invalid Label’. This totally uninformative error is the result of not wrapping the JSON object in a function call. In other words, if you have existing server code that writes JSON objects, it won’t just magically work with JSONP. Your server code must wrap a function call around the object, as I’ve shown.

Because JSONP relies on the script tag, it doesn’t use the callback features of the underlying HTTPRequest object. This means the success, failure, complete callback functions on the $.ajax() method are irrelevant and nonfunctional when you use JSONP.

37 thoughts on “How to access cross-domain data with AJAX using JSONP, jQuery and PHP

  1. Ruan

    Magnificent article!

    I was Google-ing over and about for the last 2 hours trying to find some sort of solution in calling remote files on local machines.

    This article, being precise and to-the-point, really helped me overcome this hurdle, and I’m well on my way implementing it in my project.

    Can’t thank you enough buddy.

    Well done 😉

  2. noobidyshome

    Thanks, I too spent hours looking for a good example of this – my mess worked within minutes of reading this – key info, structured well – excellent.

  3. Neil B

    Awesome !!
    Go apply for a job at jquery. Because you have done a better job braking this stuff down and explaining it in a clear concise manner.
    Very useful for me ! Thanks so much …

  4. Pingback: JSONP with jQuery – getting started |

  5. Pingback: Using JSONP with JAX-RS | Nabil Hachicha

  6. Frank

    I am new to Javascript/JSON. I’ve been fighting why my callback was not working. Your simple definition of the return structure for JSON versus JSONP made ALL THE DIFFERENCE. I now parse for the “callback” argument and use it’s value as a wrapper on my returned JSON string. That did the trick! I can now work with either a straight JSON call, or a JSONP call!!

    I actually wrote my stuff in CLASSIC ASP, yet your information made it FINALLY work!

    THANK YOU!

  7. Roberto

    Sir, you have made my day… I am trying Sencha Touch, which uses jsonp… and I did not understand the same part as you. Thank you very much!

  8. John Champion

    Who would like to help ?

    PHP is as follows

    jQuery is as follows

    $(“.submitnumber”).click(function(){
    $.ajax({
    type: “POST”,
    url: “http://www.snoopcaller.com/app/hlrencodemobile.php”,
    data: {
    number: $(“#number”).val(),
    },
    success: function( html ){
    $(“#hlrresults”).append(html);
    },
    jsonp : “callback”,
    error: function() { alert(‘Failed!’); },
    });
    return false;
    });

    and still no salvation

  9. conde

    Sir, I’ll like to show an alert of the final result, how do I go about that am new to this bur your explanation is the
    Best in years to come.

  10. conde

    I got it working ;(|) am so happy. After 5days of sleepless nights. Sir a praying for u. Tnx a bunch. Wissh I hadd a masster card.

  11. Stewart Lynch

    A bit late to this party, but it is possible as well to use the getJSON method to pass additional parameters so you do not have to use the ajax method.
    Your second example could be written like this:

    $(“#jsonpbtn2”).click(function() {
    var surl = “http://www.fbloggs.com/sandbox/jsonp.php”;
    var id = $(this).attr(“id”);
    var args = “callback=?” +
    “&id=” + id
    $.getJSON(surl,args,jsonpcallback);
    });

  12. Yuga

    Thank you very much, especially for server-side explanation :).
    One may use json_encode(array(‘message’=>’My message’)) as well, instead of object initialization.

  13. Jonno

    Awesome, thanks dude, your article really helpful! It almost drove me crazy tried to figure out why it wouldn’t get a single simple value from a JSON response put across other domain.

  14. James Star

    Hello,

    Another question re: json code.

    Can it handle any other method type? E.g. POST, PUT, DELETE?

    If I use this with my REST, and use type: ‘POST’ or type: ‘PUT’ it still only uses ‘GET’.

    Is there anything I am missing in my jQuery AJAX call?

    Thank you,
    James

  15. Jovi

    Thanks a lot if you could put in a complete downloadable code it would help, but this is the most helpful explanation of jsonp i have found.

  16. Pingback: Dynamic Web Development with Ajax, jQuery, and JSONP - Dg3World

  17. carlos

    Hello, i just get the error but i have no skill to use jsonp instead of json my code is like this

    $(‘#button-payment’).live(‘click’, function() {
    $.ajax({
    url: ‘index.php?route=checkout/payment’,
    type: ‘post’,
    data: $(‘#payment-method input[type=\’radio\’]:checked, #payment-method input[type=\’checkbox\’]:checked, #payment-method textarea’),
    dataType: ‘json’,
    beforeSend: function() {
    $(‘#button-payment’).attr(‘disabled’, true);
    $(‘#button-payment’).after(‘ ‘);
    },
    complete: function() {
    $(‘#button-payment’).attr(‘disabled’, false);
    $(‘.wait’).remove();
    },
    success: function(json) {
    $(‘.warning’).remove();

    if (json[‘redirect’]) {
    location = json[‘redirect’];
    }

    if (json[‘error’]) {
    if (json[‘error’][‘warning’]) {
    $(‘#payment-method .checkout-content’).prepend(” + json[‘error’][‘warning’] + ”);

    $(‘.warning’).fadeIn(‘slow’);
    }
    } else {
    $.ajax({
    url: ‘index.php?route=checkout/confirm’,
    dataType: ‘json’,
    success: function(json) {
    if (json[‘redirect’]) {
    location = json[‘redirect’];
    }

    if (json[‘output’]) {
    $(‘#confirm .checkout-content’).html(json[‘output’]);

    $(‘#payment-method .checkout-content’).slideUp(‘slow’);

    $(‘#confirm .checkout-content’).slideDown(‘slow’);

    $(‘#payment-method .checkout-heading a’).remove();

    $(‘#payment-method .checkout-heading’).append(‘‘);
    }
    },
    error: function(xhr, ajaxOptions, thrownError) {
    alert(thrownError);
    }
    });
    }
    }
    });
    });
    //–>

    my domain is A.com and payment gateway is B.com and i can only modify my site of course
    thanks for help me

  18. Pingback: Access cross-domain data with AJAX using JSONP, JQuery | Mitchell Fang's Coding

  19. san

    Really really….Thanks a lot…my alldoubts and querries about json are cleared now..thanks again for this article..

Leave a Reply

Your email address will not be published. Required fields are marked *