JSON Feeds For Fun and Profit Part 2 - Callbacks with Twitter
In my first exploration of the JSON data interchange format, I used it in its most basic way possible. I attached a script (from delicious) to a page and simply used the built-in object created by their implementation to generate an unordered list of my recent del.icio.us posts. While it showed how easy it is to use the O, a handy, structured, Javascript Object, provided by JSON, it didn’t really illustrate how to manipulate the page with new data after page load. That is, after all, a very now thing to do, so it’s kind of important. This article will examine how to dynamically load a JSON feed and how to get data out into a usable form using a callback.
While one can load JSON using XMLHttpRequests, the real benefit of JSON is to be able to load data from a third party without worrying about the browser security model and without having to rely on a local pass through file. So, we’re going to load the data without XHR in this example. Instead we’re going to load it using dynamic script tags.
Here’s the function I’m using in my example:
function twitter_me() {
var twitter_JSON = document.createElement("script");
twitter_JSON.type="text/javascript"
twitter_JSON.src="http://twitter.com/statuses/user_timeline/rob_react.json?callback=twitter_callback&count=10"
document.getElementsByTagName("head")[0].appendChild(twitter_JSON);
}
First I create a new script element in the document, then I set the type and source. The source is the interesting part. As you can see, after the URL for the JSON document representing my twitter updates, I append a query string. The first argument for that query string (callback=twitter_callback) is where the magic happens. There I’m defining the function that will be called when the script is attached to the document. To see what happens compare the first few characters of the JSON script called with and without the callback.
Here it is, as referenced in the above code
twitter_callback([{"source":"web","text":"426 queries in 107.66 milliseconds. drupal is teh awesome",
and called without the callback (but with the count, so the data structure returned is the same)
[{"source":"web","text":"426 queries in 107.66 milliseconds. drupal is teh awesome",
Notice the function referenced in the first example. Right there, as soon as the JSON document is loaded, JavaScript executing the callback function with the JSON object as an argument. I'm a JavaScript geek and all, so I'm biased... but that's just plain cool.
So what happens next, you ask? Well, since I defined a function called twitter_callback, that function is run and I'm able to do all sorts of cool things with the provided object. Here's the function in all its glory:
function twitter_callback(twit) {
//this is the div I'm writing the content to
var t_div = document.getElementById("twitter");
//these are some other variables, mostly
//placeholders so that the code is a little clearer
var who,what,when,icon, bgcolor
//start the ul
t_div.innerHTML = "<ul>"
//loop through the twit object
for (i=0;i<twit.length;i++) {
//Look at me use the JavaScript modulus operator to do even/odd rows.
if(i % 2) {
bgcolor="#efefef"
} else {
bgcolor="#ddd"
}
//Right here, I've broken out the separate bits of the string into placeholder variables
//so you can more easily see the dot notation and array indices in place
//once you figure out the structure it's dead easy to reference data in an Object
icon=twit[i].user.profile_image_url;
who=twit[i].user.name;
what=twit[i].text;
when=twit[i].created_at.substr(0,19);
//and here I mash it all up into a fancy li
t_div.innerHTML +="<li style=’background:"+bgcolor+" url("+icon+") no-repeat’><strong>"+who+"</strong>: "+what+" ("+when+" GMT) </li>"
}
//and close the UL
t_div.innerHTML += "</ul>"
}
Here it is in action (be patient, Twitter can be slow):
Call me crazy, but that’s something like 75% away from being a widget and there are no cross domain concerns to deal with so it could easily be spread far and wide. There’s a lot to like right there
That’s it for this time. Feel free to ping me with questions or comments.
Next up, as I mentioned, one can load JSON via XMLHttpRequest, so I’ll be building an example that does just that. There’s a wrinkle tossed in with that technique that will make things, if not tougher, a little less cool (eval(), I’m looking in your direction.) More on that when I put it all together.
BobH Says:
This is very interesting, but I don’t understand two things:
1. What is creating the twit object?
2. What is calling the twitter_callback function and passing that object?
Posted: February 10th, 2008 at 2:08 pm
rob Says:
when I request the json I pass the string twitter_callback as an argument. twitter simply takes that string and inside the response turns it into a function call with the twitter object as the only argument (referenced as twit inside the function.) with that setup it executes as soon as it is attached to the page. take a look at the response from http://twitter.com/statuses/user_timeline/rob_react.json?callback=twitter_callback&count=10
Posted: February 10th, 2008 at 2:34 pm
BobH Says:
Sorry, I still don’t really get it.
If I request http://twitter.com/statuses/user_timeline/rob_react.json without the query string, I get a JSON string containing an array of 20 user objects.
If I request http://twitter.com/statuses/user_timeline/rob_react.json?callback=twitter_callback&count=10, I get a JavaScript call to twitter_callback, passing as an argument a JSON array of 10 user objects. I see that the count parameter controls the array size.
So the first thing I don’t understand is how passing that query string to rob_react.json changes the response. I have thought of JSON as a data format, not a programming language. But I think what’s happening here is that rob_react.json is a program that knows how to read the query string and change behavior accordingly. Is that correct? Is the parameter name “callback” in the query string some kind of special name?
Let’s see if I can accurately describe this process:
1. Using script A, define a new script element. Make its source attribute value be the URL of script B. Add to that URL a query string naming a function defined in script A. Add one or more other parameters to the query string to control what script B returns. (But how does script B use the parameters?)
2. Append this new script element to the head element of the current document.
3. When the document is loaded, script A executes. It adds the reference to script B to the document head.
4. Script B is loaded and returns a function call with an object passed as a parameter to the function.
5. That function call executes and calls the callback function in script A.
6. The callback function in script A traverses the object passed in from script B and modifies the current document.
Posted: February 10th, 2008 at 3:25 pm
BobH Says:
Never mind the questions above. Now I understand that for this to work, the JSON feed must come from a Web service that knows to parse the query string in the request, look specifically for the “callback” parameter and render the response accordingly.
Posted: February 11th, 2008 at 12:34 pm
rob Says:
That’s it exactly. It’s still the same data format, just this time with an-easier-to-ingest wrapper in the form of a callback.
This is definitely my preferred method of dealing with JSON. No XMLHttpRequests, no eval(), just a nice function call.
Posted: February 12th, 2008 at 11:00 am
Keith Says:
Thanks for an excellent post that has helped me no end and also saved me a lot of frustration as I was previously trying to fetch the public timeline (http://twitter.com/statuses/public_timeline.json?callback=twitter_callback).
The public timeline seems to come back without a callback wrapper. Is this correct and is there a way to code round it or is it a problem with Twitter?
Posted: August 12th, 2008 at 11:34 am
rob Says:
I’m not sure about what twitter promises when calling the public timeline, but to make it work you’ll have to grab the script via XMLHttpRequest (and a local proxy to get around domain security issues) and eval() the response into a useful javascript variable.
It’s odd that the Object literal in the response is wrapped in an Array literal.
Posted: August 12th, 2008 at 11:46 am
Keith Says:
Thanks for the swift reply
I was experimenting with retrieving stuff whilst at work using a page on my local machine so I don’t think the local proxy is a goer. Shame as the cross domain issue for Ajax is a real pain and JSON seems to be the answer.
Once again, many thanks.
Posted: August 12th, 2008 at 12:13 pm