[ZOIS] Home Page * Contact ZOIS * Technical Notes

Quick and Dirty Ajax

ZOIS Technical Note TN-2009-11-15.

Author and Audience

As the 'front-end' of web interaction becomes more and more important this TN presents some little experiments in that direction made here. In particular dynamic content and animation are explored. The reader is assumed to be familiar with programming techniques, if not fully conversant in Javascript. Written by Martin Sullivan[au], ZOIS Limited, Cockermouth.

Abstract

Ajax, a term encompassing a number of web front-end technologies has become increasingly important, forming as it does, the core of so called "Web 2.0" interactive user-content based web-sites. Although Ajax itself is not formally defined, its current understanding is rather complexed. It need not be and this paper shows how a simple dynamic web-site can be constructed with it by cutting a few corners and ignoring some of its inherent complexity.

Introduction

Much of the Web-2.0 hype surrounds Ajax. Ajax[aj] is a somewhat loose term that encompasses accessing both the Internet in general and manipulating a displayed web-page using Javascript. Specifically it has come to mean altering the style, content and presentation of the page together with using XML and XMLHttpRequest to implement an RPC like mechanism. The result is dynamic pages that do not require reloading in their entirety when interacting with some back-end. The term itself was coined by Jesse James Garrett in 2005[ap], although the technologies had existed prior to that.

Once one has read a little around the subject and, perhaps, tried a little code it is soon realised that Ajax can be quite convoluted and in particular, very Browser specific. Javascript, and indeed most of the rest of Ajax, can be a bit of a movable feast, and although things are slowly improving its support in older browsers can be idiosyncratic. These browsers do not go away because you, as hot hip programmer, want to use some fancy feature. The result is that a customer can be looking at an ugly mess in something that is only a few years old. Several versions of the code catering for several different browsers, but all doing basically the same thing are the result. Code can quickly become quite complicated and ugly and bug riddled. The communities response to this is to jacket things up in standard libraries, with these quirks hidden from the innocent programmer, who is free, quite rightly, to concentrate on the higher things. In the authors opinion, inconsequential though it is, the best of these libraries is probably jQuery[jq]. But even this fine piece of code can be quite daunting. It can seem complicated, overblown and learning curve is steep. Well, it is to old, decrepit FORTRAN programmers. Like any jacketing technology it can take you by the hand and lead you to create stuff that looks just like the examples on the original authors web-site. Which is good, but may not be what you intended.

It was therefore resolved to explore both Ajax (as loosely defined) and related technologies (think things that make may make Ajax redundant). The results of these experiments are presented here.

Materials and Platform

Programming all this occurred in Javascript and Firefox was the target platform (specifically 3.0). Testing occurred on an iMac (for Safari), and a collection of Internet Explorers, from version 6 to the current version 8. This however cannot be considered comprehensive, for important new browsers such as Chrome and Opera remain untested. As an experiment Lynx was used to assure people using this fine text-only browser that they would not be inconvenienced by techniques suggested by these experiments. As ever the primary development environment Emacs running on Linux. A PostgreSQL based PHP Apache environment supplied the dynamic content.

Method

For conveniences sake we'll separate this into animation and dynamic data acquisition.

Dynamic Data

The Ajax orthodoxy has it that XML is used to provide a data representation structure that is delivered using XMLHttpRequest. This data is then used to update the Document Object Model (DOM) using Javascript on this directly. It is also used in parsing the bits that interest from the XML message and massaging them into a form that can be inserted into the correct bit of the DOM. The DOM itself can be considered a tree-like structure, with instances that can be set and retrieved by addressing the name of the elements, often marked up with DIV or SPAN naming conventions. This can seem awfully complicated when all that is required is a small piece of text that is presented retrospectively, either after the initial page has been loaded (to speed loading times) or in response to some event such as a mouse-over or a button-press. While all this data manipulation is good and required to give full expression to the Ajax ideal, a simpler method is simply to deliver targeted preformatted HTML and directly inject it into the page. Although its name might suggest otherwise XMLHttpRequest is happy with this, and will cope with what is effectively mal-formed and illegal XML. This delivers a sizeable amount of the dynamic-data functionality but in a rather simpler fashion. That having been decided, it is found that others on the web have formed a similar conclusion and already named such an approach 'AHAH'[ah]. Such is the nature of the Internet.

There are a number of approaches which although not novel in themselves go together in this simplification. Earlier Microsoft web-browsers were poor in their support for the key XMLHttpRequest. This is largely solved by loading an ActiveX object. And therefore it was found that the easiest way of accomplishing this was to define the XMLHttpRequest should it not be found.

// Provide the XMLHttpRequest class for IE 5.x-6.x:
// Other browsers (including IE 7.x-8.x) ignore this
//   when XMLHttpRequest is predefined
if (typeof(XMLHttpRequest)  === "undefined") {
        XMLHttpRequest = function() {
                try { 
                        return new ActiveXObject ("Msxml2.XMLHTTP.6.0"); 
                } catch(e) {}
                try { 
                        return new ActiveXObject ("Msxml2.XMLHTTP.3.0"); 
                } catch(e) {}
                try { 
                        return new ActiveXObject ("Msxml2.XMLHTTP");
                } catch(e) {}
                try { 
                        return new ActiveXObject ("Microsoft.XMLHTTP");
                } catch(e) {}
                throw new Error ("XMLHttpRequest not supported.");
        }; // defn. HMLHttpRequest
} // if

With XMLHttpRequest defined one can use it. It is an asynchronous call, in that one makes a request and then some pre-defined functions are called when a response is completely received.

function ahah(url, target_id, animation) {
        if (document.getElementById(target_id).innerHTML != "") {
                animation(target_id);   // already got that
                return;
        } // if

        if (window.XMLHttpRequest) {
                req = new XMLHttpRequest();
                req.onreadystatechange = function() {
                        ahahDone(target_id);
                        animation(target_id);
                };
                req.open("GET", url, true);
                req.send(null);
        } //if
} // ahah

In this fragment, the function ahah uses XMLHttpRequest to run a remote GET, provided in this instance by PHP. The PHP function returns pre-HTML formatted code which will be inserted into the element noted by target_id. Should the target HTML not be empty then that no HTML is retrieved, but what is there already is used. Thus the target_id HTML element needs to be empty if the new content is to be fetched.

When the ahah function is called, a second function, animation is invoked. This handles the display of the new or recently acquired content.

Finally there is the ahahDone function:

function ahahDone(target_id) {
	if (req.readyState == 4) {	// only if req is "loaded"
		if (req.status == 200 || req.status == 304) { // only if "OK"
           		results = req.responseText;
			document.getElementById(target_id).innerHTML = 
			    results;
		} else {
			document.getElementById(target_id).innerHTML = 
			    "Ahah error:\n" + 
			    req.statusText;
		} // else
	} // if
} // ahahDone

This function is run only when there is a response from the remote server. Should the response be valid then the returned, preformatted, HTML is injected into a nominally empty HTML element, identified by ID. In this particular example the HTML is not displayed until the animation function is run on the ID. Thus the data is acquired and then displayed in a magical Web 2.0 manner.

Animation

Once the data has been acquired by the procedures seen above it needs to be displayed. The HTML element that the newly obtained preformatted data is injected into normally is not displayed. The animation function is run to display this, in a suitably whizzy way. In our example the element appears as a 'bubble' uncovering from the bottom. Firstly some Cascading Style Sheet code is required to initialise the element:

/* optional stuff is dynamically manipulated by Javascript in
 * conjunction with a dynamically assigned tag	*/
.optional {
    display: none;
} /* optional */

This is identified by a class construct in a DIV HTML element. This DIV identifies the empty HTML element initially and generally by CLASS. The individual element is identified by an ID. The HTML looks something like this:

<div class="optional" id="someIdElement">

Having labelled things up one can proceed to the nitty-gritty of animation. In Javascript this is not straight forward, but required a little well known recursive type jiggery-pokery.

var moving = false;             // guard flag
var last = null;

function show (s) {
        if (last)
                hide (last);    // debris from last time gone
        last = s;
        if (moving)
                return;

        window.status="display text";
        t = document.getElementById (s);

        t.style.height = '';     /* reset to initial value if run again */
// from here ...
        t.style.width = '50%';
        t.style.backgroundColor = 'aqua';
        t.style.borderStyle = 'solid';
        t.style.borderWidth = 'thin';
        t.style.left = '20%';
// ... to here defines a bubble like entity
        t.style.position = 'absolute';
        t.style.visibility = 'hidden';

The above fragment mostly sets style elements and could conceivably be replaced by an appropriately labelled style element in a Cascading Style Sheet (CSS). Two important bits of code allow the clearance of debris and ensure that any in-progress animation is allowed to progress to completion. To conclude:

        current = 0;
        moving = true;
        animate_down ();
        moving = false;
} // show

Function animate_down does most of the work.

function animate_down () {
        if (target == current)
                return;
        t.style.height = current + "px";
        current++;
        if (current > 1000 && !asked) {
                asked = true;
                if (!confirm ("Very large animation, continue?")) {
                        ask = false;    // reset
                        return;
                } // if

        } // if
        x = setTimeout ("animate_down ()", 5);
} // animate_down

Animation in Javascript can only be accomplished with a lame tail recursion that is tracked by a global variables. Thus the current height of the animation is kept in a variable 'current' and, as tradition demands, is tested to see if the recursion is completed. If not the appropriate style element is adjusted and the function re-called via a timer function, setTimeout. In the above example some guard code ensures that the animation does not overrun by crudely checking the current size of the animation against an arbitrary large value.

function hide (s) {
        var t = document.getElementById (s);
        t.style.display = 'none';
} // hide

The hide function simply removes the element from view. This was found to be the least surprising. An animation at this point only slowed things down for the user. The function is designed to be called in advance of a new call to animate_down, thereby removing any previously displayed element from the user experience. Hide is also used to remove distractions when the user moves away from the element under consideration and when the user clicks through of the full details. The example HTML for this is:

<a href="jcpdetail.php?reference=WHV%2F6851"
        onMouseOver="ahah ('jcpsumm.php?reference=WHV%2F6851',
           'WHV%2F6851',
           show)"
        onMouseOut="hide ('WHV%2F6851')"
        onClick="hide ('WHV%2F6851')"
        >Outpatient Co-ordinator LEP</a><div class="optional"
	id="WHV%2F6851"></div>

Thus, putting it all together in the above example (taken from a real web-site[hm]), Should the user place their pointer or mouse cursor over the anchored element then jcpsum.php is run with an appropriate reference. This returns a small piece of pre-structured HTML and it is injected into the "optional" element identified by the "WHV%2F6851" ID. The function show is then called to check and perform a fairly rapid animation that will cause the element to appear in a bubble like over-box. Should the user user either click on the anchor or remove the mouse cursor from the link then the element is perfunctorily removed.

Discussion

The code can be seen in demonstration on the Home web-site[hm]. Since it is interpreted it is open to inspection.

Although it looks simple it is in fact the result of some experimentation on the part of the author. These experiments did allow the formulation of some opinions on the design of Javascript driven web-pages.

The major finding of this work is that Ajax and related technologies add complexity to web-sites that frequently detract from the user experience. The Author is in the habit of visiting web-sites with Javascript switched of, either in its entirety or through the good offices of NoScript[ns]. This means that he frequently visits web-sites that are catatonic or format badly because both style elements and necessary functionality are embedded in Javascript scripts. No less irksome are web-sites that disallow functionality (including trying to sell you things) until you've switched Javascript on. There is presumably a pressing need for the users of such sites to be dazzled by the programming ability of their authors.

So, with the author of this papers somewhat jaundiced opinion to the fore, we can see that he favours a very conservative style of Javascript programming that only enhances an existing web-site. To this end experiments were undertaken that caused large animations to occur, with blocks of text being moved around the page and reformatted on the fly. It was found that support of such features was poor, particularly in older browsers and after an initial 'Wow' phase, irritating.

In our quick-and-dirty Ajax we download a single fragment of preformatted HTML to be injected into the web-page. This works, but it does mean that some parts of the presentation layer is taken away from the browser and Ajax based program. A better solution is to use XML or JSON to add structure to the data and then parse it into the appropriate place in the Document Object Model. This is the 'correct', heavy way of doing things in Ajax. Not only does it allow multiple fetches of data, improving efficiency, but it decouples the presentation layer, which is always a good thing.

Although, in this paper, a simplified Ajax is presented anything more complicated rapidly becomes challenging. It is particularly true once multiple browsers are to be catered for. For this reason several Ajax libraries of Javascript have been produced. The authors favourite is jQuery[jq], but by no means is it the only one. There are a number of others and comparison is invited[jf].

References

References found in this section, and in particular the HTML links were correct at time of writing (2010-03-03).

[au]. Martin Sullivan:
http://www.zois.co.uk/people/martin_sullivan
[aj]. What is Ajax?
http://www.riaspot.com/articles/entry/What-is-Ajax-
[ap]. Ajax: A New Approach to Web Applications:
http://www.adaptivepath.com/ideas/essays/archives/000385.php
[jq]. jQuery:
http://jquery.com
[lx]. Linux:
http://en.wikipedia.org/wiki/Linux
[ph]. PHP:
http://php.net
[em]. Emacs:
http://www.gnu.org/software/emacs
[ah]. AHAH:
http://microformats.org/wiki/rest/ahah
[hm]. Javascript example on Home:
http://home.zois.co.uk/jcpjs
[ns]. NoScript:
http://noscript.net
[jf]. Comparison of JavaScript frameworks:
http://en.wikipedia.org/wiki/Comparison_of_JavaScript_frameworks

$Date: 2010/03/07 10:17:13 $


Break Frame * E-mail Webmaster * Copyright