The website I have been working with, Asylum.com, recently expressed interest in adding a Digg Most Popular widget to their site. Asylum.com asked me to apply some CSS to resolve the display issues they were having. After I spent a little while investigating the HTML markup in the standard Digg widget, I decided it wouldn’t be in Asylum’s best interest to use it in it’s current form.
While the widget is well done, it used a few techniques that are frowned upon at AOL. The most disconcerting issue was the use of document.write(). If you are familiar with JavaScript, then you are aware that document.write() causes issues on a few levels. First off, document.write() blocks while it is working. This blocking prevents other items on the page, such as CSS or images, from loading while the write occurs. Additionally, elements and text created via document.write() are not added to the DOM ( Document Object Model ) and cannot be manipulated in the same way. I envisioned code that was more friendly to both the page load and the DOM.
In addition to document.write(), the Digg Widget was calling in the entire jQuery framework. This seemed like overkill. I didn’t want to include an entire library just to create this widget.
The Digg API lives on the Digg server, I couldn’t use classic Ajax because of the same origin policy. However, Digg provides JSONP (JavaScript Object Notation with Padding). This allows you to wrap the JSON with a callback function that will be executed once the script loads. To use JSONP, you have to dynamically create a script tag and assign the Digg API url to the src attribute. Most modern browsers support dynamic creation of script tags, it would look as follows.
var s = document.createElement( "script" );
s.setAttribute('src', apiURLAndArguements );
s.setAttribute( 'type', 'text/javascript' );
var h = document.getElementsByTagName( 'head' );
h[0].appendChild( s );
The Asylum team asked that the Digg widget have both the most popular articles and the upcoming articles contained within the widget. Since I didn’t want to write the same code twice, I sought an Object Oriented, self-contained widget that could use the same JavaScript blueprint to use both calls.
The biggest obstacle to creating this was JavaScript scope. If I instantiated an object to contain the Digg data, I would lose scope the moment the callback function was triggered. The callback would be called in the scope of the window and not the scope of the object that first created the script tag.
The Digg API further complicated this because it does not allow periods, square brackets or curly brackets in the name of the callback function. I spent some time researching JavaScript inheritance and closures hoping they would provide the resolution I needed.
In the end, I used a combination of JavaScript closure and JavaScript function pointers to provide the solution. The key lay within providing the callback function a correlation to the original object. Since Digg doesn’t allow periods and square brackets in the name of the callback function, I couldn’t create a simple global array with a reference to the object. Instead, I had to generate a function name appended with a random string that would live in the global scope. The function interior would look as follows.
var method = this; // a reference to the calling object
function wrapperFunc( obj )
{
method.returnDataHandler( obj ); // the function to be triggered within the original calling function
}
var rand = Math.ceil( Math.random() * 100000000000 );
window['DiggAsylumCallback' + rand] = wrapperFunc; // store this pointer in the global scope
var urlBase = "http://digg.com/tools/services?type=javascript&callback=DiggAsylumCallback"+rand+"&endPoint="+ finalEndPoint +"&domain="+ yourDomain +"&sort=digg_count-desc&count=" + numberDisplayed
return urlBase;
Once the callback function triggers, it stores the JSON inside the original calling function and the prototype methods can be called in the correct scope. Here is a look at how the object is triggered.
var popular = new diggAsylum( 5, "asylum.com", "/stories/popular", "diggDivId", true, "desc" );
The diggAsylum constructor signature looks as follows.
function diggAsylum( count, domain, endPoint, htmlId, showDesc, sortType ) { .... }
Get the complete JavaScript file here, see it in action here, get a zip file of the CSS, JS and HTML here.