Modular JSON/On-Demand JavaScript interface
In this article I hope to give an overview of a number of interesting techniques used in the construction of the MapSurface prototype. The core of the design is a modular architecture which is built using a combination of on-demand script loading and JSON data transfers.
MapSurface has a widget called the Dashboard which can be loaded into web pages sitting above the general content. It is basically a secondary 'in context' interface. I have used this interface to display web page activity information, but it could also be used to provide page level administration functionality.
If you press the “Alt” and “X” keys the MapSurface Dashboard appears. If you are interested, I have written a more general post about the MapSurface features. You can add the functionality of MapSurface to any site, by the inclusion of a single JavaScript file link, in any page you wish to track.
Module Architecture Overview
The loading file
When you sign up for a MapSurface
Account you are given a JavaScript file link appended with a unique account
number
<script language="javascript" src="http://www.mapsurface.com/api/1.0/mapsurface.aspx?accid=0002xsdi" type="text/javascript"></script>
This file contains code to provide two different functions. The first is to gather as much information about each request as possible. The second is to start the construction of the Dashboard when ever it's requested.
Sending request information to the server
It sends the request information back to the server based on three events. The
page load and unload and the mousedown on any hyperlink. As this information is
sent in a fire and forget mode, I have chosen the simplest method of communication
with the server, a request for an image. Each request is appended with a
querystring.
function msAddImage( passedurl )
{
eltImg = document.createElement( 'img' );
eltImg.setAttribute('id','tackerloadimg');
eltImg.setAttribute('width',1);
eltImg.setAttribute('height',1);
document.body.appendChild( eltImg );
addEvent(eltImg, 'load', msRemoveImage , false);
urlcompsite = passedurl + '&rand=' + Math.random();
eltImg.setAttribute("src", urlcompsite);
}
function msRemoveImage()
{
if( document.getElementById('tackerloadimg') )
{
eltImg = document.getElementById('tackerloadimg')
eltImg.parentNode.removeChild(eltImg);
}
}
The image is appended to the bottom of the body element and then a load event is attached to the new image. The funtion called by this load event will remove it from the body.
On-demand script loading
On-demand script loading is a technique where you use JavaScript to add
a JavaScript file to the current page. Using this approach you can divide
your JavaScript into modules that provide distinct functionality. The
initial page load is very fast as you only need the code to provide the
on-demand loading. The subsequent loads are also fast as you are only load the
smallest required amount of JavaScript.
AddScript function
There are a number of libraries that have on-demand script loading functionalities,
but I have used my own version for a couple of reasons. I have added a random
query string attribute to stop the JavaScript being cached. The second difference
is that I have removed any checks to see if the file has been previously load.
This allows me to overload JavaScript files that contain data.
function msAddScript( url )
{
eltScript = document.createElement("script");
eltScript.setAttribute("type", "text/javascript");
if( url.indexOf('?') > -1)
url += '&';
else
url += '?';
url += 'rand=' + Math.random();
eltScript.setAttribute("src", url);
document.getElementsByTagName('head')[0].appendChild(eltScript);
}
Separating presentational code and data
As each module loads it needs both presentation code to build the interface
elements and data to populate them. I decided to split the code and
data into different files. This allows me to refresh the data or to display it
in more than one way. The data is returned using the
JSON format. JSON is a
lightweight data format based on a subset of JavaScript. It basically
describes an object with a collection of name/value pair’s i.e. {“Total”:345}.
What no Ajax!
I
could have chosen Ajax as the method of retrieving data from the server, but it
does not work across domains. You can only make a XHR (XMLHttpRequest) call from the
same domain as the HTML page came from. This security feature
limits Ajax and although there are methods around this issue, (server-side
proxies) none of these are practical for this application.
Using callback functions
I have followed the Yahoo model of providing the server API with the
ability to specify the name of a callback function. The API’s callback parameter wraps the JSON output in
parentheses and a function name. This means as soon as the data
JavaScript file loads successfully it call’s a function in the presentation code.
The JavaScript src attribute would be something like;
http://www.mapsurface.com/api/1.0/getURLRequests.aspx?callback=msVPLoadViewsData
which returns a text stream containing
msVPLoadViewsData( {“Total”:345} );
Because JSON output is in a JavaScript object format, you do not have to parse or evaluate the JSON text in your function. The object is just passed directly into the function as in the example below
function msVPLoadViewsData( obj )
{
if( obj == null )
alert('load error' );
else
alert( obj.Total );
}
Building the interface
MapSurface is a piece of unobtrusive DOM Scripting which follows the
page-hijacking technique. The interface is created by directly appending
HTML elements into the DOM. I did consider using the innerHTML property to inject
downloaded HTML segments, but I believe that using DOM scripting is a much more
flexible approach. I can use one piece of code to produce multiple variants of
an interface part i.e. bar charts.
Loading CSS on-demand
As well as the additional HTML elements created by DOM Scripting I created a
single stylesheet for each module. These where loaded using the same technique as the on-demand
JavaScript. I created a new link node and appended this to the header of the page.
var newstyle = document.createElement("link");
newstyle.setAttribute('rel',"stylesheet");
newstyle.setAttribute('type',"text/css");
newstyle.setAttribute('href', url);
document.getElementsByTagName('head')[0].appendChild(newstyle);
The backend system
The backend system stores all the request information sent from the client and where possible extends what has been collected by the browser. The API calls return JSON, but can be configured to output RPC-XML. I have provided this additional format in case I wished to extend the functionality of the site.
URLs of interest
http://www.crockford.com/JSON/
http://developer.yahoo.net/common/json.html
http://ajaxpatterns.org/On-Demand_Javascript
Note
I will be developing different commercial applications from these concepts
through my company Madgex. I am unsure about the path of the MapSurface
prototype itself, if there is enough interest it could be developed into a
commercial service. So for now, apart from the code display on this page I am not making the MapSurface code open source.
Comments
1
Shaun Inman
Nice write up Glenn. Quick question about the msAddImage function. Is there any reason you're using the DOM to insert an actual image element instead of just using an old school, image preloader script?
Eg.
var img = new Image();
img.src = passed_url;
Btw, it was great meeting and chatting with you at the Sanctuary.
Posted 15 February 2006 01:58
2
Glenn
Thanks Shaun, Recently I have been coding JavaScript using DOM Scripting wherever possible. It has made me overlook a simpler solution. Changing the msAddImage function over to the old school preloader would be a good idea. The function would then work with a wider set of browsers. Sometimes old school is best!
It was nice meeting you, hopefully I will see you at SXSW.
Posted 15 February 2006 11:10
3
Jerry Mead
Glenn, you *have* to check out Zeepe, you will love it.
http://www.zeepe.com/
Give me a shout if you need a more flexible demo/dev license.
Jerry
(Ex-proprietor of a Kemp Town junk shop in the late 60s :-)
Posted 22 February 2006 14:38
4
Shaun Inman
Glenn, I would hold off on reverting to the old school approach. I've had reports of inconsistent tracking as a result of the change in Mint. That said, I haven't been able to reproduce the issue and observe it first hand. To be safe I've reverted to my original approach for now. I don't want to be responsible for sending you down the wrong path too!
Posted 24 February 2006 18:40
5
Ryan
Hi,
I notice that the dashboard overlay(s) do not cover certain form controls (dropdown lists) when using Internet Explorer.
This is a known 'feature' with Internet Explorer that can be fixed by simply creating an IFrame to appear in between the page (original layers) and the dashboard overlay (z-index).
Of course the IFrame would need to appear at the same position and be the same size as the dashboard, and have a solid background colour (not transparent).
You would also need to move the IFrame wherever you move the dashboard.
Ryan
Posted 04 April 2006 13:53
6
Vishal Raj
Hi,
I have a very typical type of problem.
I am using json to add new HTML elements to my form.
Now when the form is submitted, none of the elements, added to form using JSON are recieved in PHP.
Can any body please help me out.
Vishal Raj
Software Developer,
Naukri.com
Posted 28 June 2006 11:56
7
murty
good
Posted 09 January 2007 09:54
8
Vicky Biswas
Hey Vishal javascript is loading it on the client side where as PHP resides on the server side, to transfer anything between them you would have to make use of cookies.
Posted 01 July 2008 08:49
9
Vicky Biswas
or Pass using either GET, POST or AJAX
Posted 01 April 2009 14:54
Comment Guidelines
All comments are moderated. Please keep comments relevant. Abusive, inappropriate and anonymous posts may be edited and/or removed.