Skip to content

February 3, 2010

2

Cross-Domain Ajax with Dojo

If you are fairly new to JavaScript development or Ajax programming, you may not know that JavaScript has a limitation in which it cannot make a remote call to a service that is not on the same domain the script was called from. For instance, if I hosted a simple web service at kylehayes.info, I would not be able to access it from this site through the normal methods such as dojo.xhrGet() or dojo.xhrPost(). However, there are a couple of solutions to this issue. One of those is to consume web services that use a format called JSON-P (JSON with padding).

Simply put, this method of retrieving data from another domain is done by using a script tag on your page that loads the external file that “executes” a named function on your calling page passing it data. Let me show you an example.

Flickr is a great example of a company that offers it’s web services in a JSON-P format. For instance, a few lines of their interestingness feed looks like:

jsonFlickrApi({
   "photos":{
      "page":1,
      "pages":5,
      "perpage":100,
      "total":500,
      "photo":[
         {
            "id":"4325811507",
            "owner":"75345140@N00",
            "secret":"9d963f8778",
            "server":"2702",
            "farm":3,
            "title":"Apple iMaxiPad (development name iAnvil)",
            "ispublic":1,
            "isfriend":0,
            "isfamily":0
         },
         {
            "id":"4325354439",
            "owner":"34991336@N00",
            "secret":"4803bbeb7a",
            "server":"2767",
            "farm":3,
            "title":"San Francisco's Faces Teaser",
            "ispublic":1,
            "isfriend":0,
            "isfamily":0
         }
...)

Notice at the beginning of the statement it begins with jsonFlickrApi(...) which looks just like a function call right? That’s because it is. In order for it to work though, we need to define that function in our page first so that when this code loads, it can call the function and pass in the data. Start off by creating a function on your page with the same name of the function that your service will call (known as the callback) and give it an argument that the data will be passed in as. Immediately after it, put a script tag that loads your external data. I’ll use the interestingness feed from Flickr.

<script type="text/javascript">
var jsonFlickrApi = function(data) {
}
</script>
<script src="http://api.flickr.com/services/rest/?method=flickr.interestingness.getList&format=json&api_key=9debd0de9deea0e59b4b8ae22a69078a"></script>

At this point, if you were to load your page, nothing would actually happen since the function doesn’t do anything once it’s called. If you’ve gotten this far without JavaScript warnings from your browser or Firebug, then you are in good shape. Let’s flesh out the jsonFlickrApi function with actual logic to work with the data that gets passed to our function. All we want to do is create a variable that will hold the contents of our data, in this case it will be the photo information that Flickr returns. Create a global variable that will hold the data, then assign it the data once the function is called:

var photoData = null;
var jsonFlickrApi = function(data) {
    photoData = data.photos.photo; // the array of photo information objects that Flickr passes back
}

Running the script now would load the array of photo information into our global variable called photoData. Once this is loaded we can actually play with the data by displaying the photos on our page. This entails adding an element on the page that will hold our image elements and looping through the photos that were returned and placing them in the page. This final product will look like the code below

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
 
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>interestingness</title>
	<style type="text/css">
	#imgs img {
	  display: block;
	}
	</style>
	<script src="http://o.aolcdn.com/dojo/1.4/dojo/dojo.xd.js"></script>
 
	<script type="text/javascript">
    var photoData = null;
  	var jsonFlickrApi = function(data) {
  	  photoData = data.photos.photo;
  	}
  	dojo.ready(function(){ 
  	  var imgsBody = dojo.byId('imgs');
  	  dojo.forEach(photoData, function(photo){
  	    dojo.place(dojo.create("img", {src: "http://farm" + photo.farm + ".static.flickr.com/" + photo.server + "/" + photo.id + "_" + photo.secret + ".jpg"}), imgsBody);
  	  });
  	});
	</script>
	<script src="http://api.flickr.com/services/rest/?method=flickr.interestingness.getList&format=json&api_key=9debd0de9deea0e59b4b8ae22a69078a"></script>
</head>
<body>
<div id="imgs"></div>
</body>
</html>

View a working example of the above code

The aforementioned explanation is how you could make a JSON-P call from any JavaScript toolkit (substituting the appropriate API calls where necessary). However, Dojo has built in functionality to make this a bit easier for the developer. The function to look at is dojo.io.script.get(). This allows us to provide a JSONP url, a callback name to use to pass a callback function name, and define a load function that will be used when the script has loaded successfully. An example of this is below:

    dojo.require("dojo.io.script");
    dojo.ready(function() {
      dojo.io.script.get({
       url: "http://api.flickr.com/services/rest/?method=flickr.interestingness.getList&format=json&api_key=9debd0de9deea0e59b4b8ae22a69078a",
       callbackParamName: "jsoncallback",
       load: function(data) {
         var imgsBody = dojo.byId("imgs");
         var photos = data.photos.photo; // array of photo info
         dojo.forEach(photos, function(photo){
     	     dojo.place(dojo.create("img", {src: "http://farm" + photo.farm + ".static.flickr.com/" + photo.server + "/" + photo.id + "_" + photo.secret + ".jpg"}), imgsBody);
     	   });
       }
      });
    });

We start out by importing the dojo.io.script package for use on our page. Then we add a handler for when the page is done loading using dojo.ready (which is simply a proxy function for dojo.addOnLoad). From there we make a dojo.io.script.get() call, passing in an object of properties and values that define our XHR call. You’ll notice that the Flickr URL is now defined in this list instead of a separate &lt;script&gt; tag. This is the beauty of this function as it allows us to keep our code cleaner and provide the functionality through JavaScript only. In the background, however, Dojo is handling the dynamic creation of the script tag similar to what we used in the first part of this demo.

The actual functionality for this execution didn’t change and when you run the demo for the code above, you’ll notice nothing different except for the underlying logic.

2 Comments Post a comment
  1. Amir
    Dec 15 2011

    Thanks, this page is very well written.

    Amir.

Trackbacks & Pingbacks

  1. Cross-Domain Ajax with Dojo | 007Nova Articles

Share your thoughts, post a comment.

(required)
(required)

Note: HTML is allowed. Your email address will never be published.

Subscribe to comments