Monthly Archives: January 2006

JavaScript: AJAX

Continuing in my series of articles on my JavaScript library, I am doing my second article about my AJAX JavaScript file. The first article was written right at the start of the AJAX craze, and was (in hindsight), rather badly written. The JavaScript provided with it was too, quite badly written, and I've since revised my JavaScript (without telling anybody) several times. This morning, version 2.0.0 landed safely in my private SVN repository, and it is this that I am going to talk about.

Since the first version of my AJAX code, two major changes have been made (and many minor ones). The first change was to abolish the awful <resultpos> idea, splitting each function in two (i.e. when adding a comment, instead of having the same function called on the client side originally and when the data packet is received from the server, two separate functions would be called). The second major change was completed only recently, and has enabled multiple simultaneous requests to be made without fear of the packets getting mixed up.

Also present in this version is new functionality in the form of AJAX popovers. No longer are complete pages limited to being loaded through an HTTP request; using the popover functions in this script will enable you to have pages load as popovers on top of the current page.

This functionality isn't meant to be used for advertising (certainly not!), but the terminology was too good to lose. Imagine a scenario where you have a user who wants to log in, but making a complete HTTP request is just too much of a drag for them, and it's flashier to do it using that wonderful buzzword: AJAX. The user clicks the "Login" link, and instead of a new page loading, and them losing where they were in the current page, a popover appears with the username and password inputs. The user duly enters their details and clicks "Login", to have the login popover fade out, and their login to be authenticated.

Admitted, it doesn't have a massive range of uses, but it certainly is good for adding that "wow" factor to a website or application.

So…on to the script file itself. The script doesn't require any other files for pure AJAX usage, but if you want to use popovers, you'll need to include my general JavaScript library, and if you want fading, you'll need to include the GUI script file I previously wrote about.

To include the script in your page, you'll need to add the appropriate tag to the <head> tag of your document:

<script type="text/javascript" src="http://tecnocode.co.uk/sources/client/ajax.js" />

However, this file on its own won't really get you anywhere: you still need some CSS to prettify the popovers, and another script file to contain implementation-specific AJAX JavaScript. This file, you'll have to write yourself (typically producing ajax_custom.js), but I've provided some CSS already, which can be included by placing the following line in your <head> tag:

<link href="http://tecnocode.co.uk/sources/client/ajax.css" rel="stylesheet" media="all" type="text/css" />

Use of the script is quite simple. To initiate an AJAX transaction, simply call the load_XML_doc() function, passing the URL you want to load (with any data you want to pass as escape()d GET parameters) as the only parameter. The function will perform the transaction, calling the JavaScript callback function specified in the packet when the XML data packet is received.

Each XML packet must be in the following format:

<response>
	<method>[callback function name]</method>
	<result>[result markup]</result>
</response>

This latest version of my code also supports messages and errors returned by the server, using the first following XML packet for messages, and the second following XML packet for errors:

<response>
	<message>[message text]</message>
	</response>
<response>
	<error>[error code or name]</error>
	<message>[error message text]</message>
</response>

Errors and messages are handled transparently by the AJAX code, so implementation-specific code need not worry about it.

Speaking of implementation-specific JavaScript, it is a bit of a hazy subject, so an example would be in order. The example I'll use is the AJAX comment submission code form this very website, which requires several of my JavaScript libraries not previously explained. You'll just have to live with that.

Implementation-specific JavaScript is split into two functions: a function called to build the request URL and initiate the transaction, and a function to be called when the XML packet is received from the server.

This first function takes six parameters, specifying the details of the comment: author, email, comment, rating, post_id, parent_comment. The function is called in place of submitting the comment form, with the last two parameters hard-coded into the markup by the server:

function add_comment(author,email,comment,rating,post_id,parent_comment)
{
	//Take the specified input, and make the query
	if(!check_form(form)) return false;
	var url="./index.php?media=xml&page=blog&paragraph=add_comment&id="+escape(post_id)+"&parent_comment="+escape(parent_comment)+"&author="+escape(author)+"&email="+escape(email)+"&comment="+escape(comment)+"&rating="+escape(rating);
	load_XML_doc(url);
	return false;
}

Note that the function also calls check_form, to make sure all the fields are filled in correctly. This function is from my form validation JavaScript, which I hope to write about in the future.

So, this function has started the request, and now the client is waiting for news of the packet. Usually, this will be very fast (that's why I decided not to put an activity indicator on the page), but sometimes it won't; you might want to consider putting a little activity indication "throbber" into pages when using AJAX.

The server processes this request. In this case, it takes the comment data which has been submitted (notice that it was all escape()d), checks if it's all valid, and if so, writes it to the database. It then returns an appropriate XML packet, with a <method> of add_comment (not add_comment_response: the "_response" is added on the client-side). add_comment_response is then called:

function add_comment_response()
{
	//Response mode: parse the response, and see what the server says about our submitted comment
	//Our <result> will be the HTML for the comment (boolean)
	if(AJAX_RESULT)
	{
		//Display the comment, remove a class="no_x" element (if applicable) & clear the form fields (make sure we don't clear locked ones)
		var container=document.getElementById("comments_container");
		var comment_form=document.getElementById("comment_form");
		var i,random_id,cloned_child;
		var elements=getElementsByClassName(container,"no_x_comments");
		for(i in elements)
		{
			random_id=assign_random_id(elements[i]);
			fade_object(random_id,0.1,20,false);
			setTimeout("document.getElementById('comments_container').removeChild("+random_id+")",240);
		}
		for(i=0;i<AJAX_RESULT.childNodes.length;i++)
		{
			cloned_child=AJAX_RESULT.childNodes[i].cloneNode(true);
			if(cloned_child.style) cloned_child.style.opacity=0;
			container.appendChild(cloned_child);
			if(cloned_child.style)
			{
				random_id=assign_random_id(cloned_child);
				fade_object(random_id,0.1,20,true);
				wait(240);
			}
		}
		clear_form_inputs(comment_form);
	}
	else
	{
		window.alert("Your comment was rejected by the server. Please amend any errors and try again.");
	}
}

This function isn't passed any parameters. (Perhaps it should be? I'm looking into revising this functionality sometime in the future.) It accesses the global variable AJAX_RESULT to see what's there. In this example, the result contains all the markup for the submitted comment, so that it can just be popped (using the DOM) into place after all the other comments in the page.

Half of the complexity of this function is due to the use of fading from my GUI JavaScript, and the use of the DOM to insert the retrieved markup into the document instead of using outdated methodologies such as innerHTML.

Another feature of version 2 of my AJAX JavaScript is the popover functionality. It allows you to create popovers to perform tasks related to (or required by) the current page without refreshing.

The markup for the popups is retrieved when required from the server, reducing the amount which has to be initially downloaded. For example, you could have a "Login" link, which has an onclick handler as follows:

<a href="./?page=login" title="Follow this link to login." id="login_link" onclick="Javascript: return load_XML_doc('./?page=login&ajax');">Login</a>

Without JavaScript, it just takes you directly to the login page, but if you have a capable browser, it loads up a special version of the page (encased in one of the previously-described XML packets) through AJAX.

The return packet would have a <method> of "login", which would call the first of the two custom JavaScript functions required to do a popover (you can cut this down to one function if it's a non-interactive popover, such as a message):

function login_response()
{
	//Instantiate a popover
	if(AJAX_RESULT)
	{
		var login_link=document.getElementById("login_link");
		var x_pos=findPosX(login_link);
		var y_pos=findPosY(login_link);
		if(AJAX_RESPONSE.getElementsByTagName("redirect")[0])
		{
			setTimeout("window.location='"+AJAX_RESPONSE.getElementsByTagName("redirect")[0].textContent+"';",2000);
		}
		do_ajax_popover("Login",AJAX_RESULT,x_pos,y_pos);
	}
	else
	{
		window.alert("The server could not return the login form. Please try again.");
	}
}

This code is called once the markup has been retrieved from the server, and works out the position at which to spawn the popover, then spawns it. In this example, the position is worked out as the position of the login link the user followed, but other things may be done.

The popover has now been displayed; what to do now? Well…you'd want to have the login carried out through AJAX too, just to capitalise on the situation. This can be done. :)

The first child node of the popover is always given a special attribute: popover_id. This contains the unique ID of the popover containing the markup, so that multiple popovers can utilise the same JavaScript simultaneously without getting tangled. In this example, the login form calls a JavaScript function (do_login) instead of being submitted normally, although it can fall back to that if required:

function do_login()
{
	if(document.getElementById("login_form").getAttribute("popover_id"))
	{
		var url=document.getElementById("login_form").getAttribute("action")+"&ajax&username="+escape(document.getElementById("username").value)+"&password="+escape(document.getElementById("password").value);
		load_XML_doc(url);
		close_ajax_popover(document.getElementById("login_form").getAttribute("popover_id"));
		return false;
	}
	else return true;
}

This is basically just another URL-generation function, with the addition of a call to close_ajax_popover with the popover ID to close the popover after the login data has been submitted. Once the return packet arrives for this request, it will call login_response again (as it will have a <method> of "login"), but it will also have a <redirect> tag present, which will redirect to the logged-in page after it has displayed the message which was returned by the AJAX request. It's a bit of a mouthful to get your head around. ;)

I have put together a small sample page showing popovers in a little more detail (i.e. interactively).

Now for a more technical view of the functions:

Function declaration Description
public bool load_XML_doc(string url) Request the specified URL through an XMLHttpRequest object. Return false on success, and true on failure (for events).
private void process_request_change() Processes a data packet received from the server after a request.
public bool do_ajax_popover(string title_text,dom_tree snippet,int x_pos,int y_pos) Create a popover and make it appear in the page.
private void _close_ajax_popover(dom_event event) Destroy a popover after the "Close" button has been pressed.
public void close_ajax_popover(string popover_id) Destroy a popover by ID.

The CSS is quite simple, and all the classes and IDs generated by the Javascript already have a rule in the CSS file. I have provided a brief explanation of each class, and an image to depict what it is:

Class / ID Description Applicable element(s) Descriptive image
.popover_container The main popover "window" element. <div> .popover_container
.popover_content The content in the popover, retrieved from the server. <div> .popover_content
.popover_bottom A currently unused element at the bottom: could possibly be used for a "tail". <div> .popover_bottom
.popover_title The popover's title. <h2> .popover_title
.popover_close The "Close" button. <a> .popover_close

If you do use this script, please download it and host it with your website; do not leech my bandwidth! Additionally, please follow the terms of the license to the letter. I hope this is of use to somebody. :)

JavaScript: GUI

Following on from my article on JavaScript popup messages, I am going to try and get through my entire JavaScript library, releasing it to the public. This is a good thing all round, as I get to improve my JavaScript to bring it up to release quality, and then it gets released. :)

This script file is the GUI library. Although there are many functions for dealing with the interface in other script libraries, the functions in this file are the most so, and aren't relevant to anything else.

It doesn't require any other library files, although having it as the only library file won't get you anywhere quickly, so it would be best to include my general library first, by putting the following markup in your <head> tag:

<script type="text/javascript" src="http://tecnocode.co.uk/sources/client/general.js" />

The next step is obviously to include the GUI JavaScript itself, which is best left untouched, but that's by no means absolute:

<script type="text/javascript" src="http://tecnocode.co.uk/sources/client/gui.js" />

Currently (January 2006), there are only three functions in the file: a function to fade things in and out, and two functions for retrieving the position of an element. These two functions aren't actually mine; credit for them must go to quirksmode.org, although I'll probably customise them in the future.

To fade an element in, you must first set its opacity to 0:

element.style.opacity=0;

Then, you can use the fade_element function to gradually fade the element in:

fade_object(element.getAttribute("id"),0.1,10,true);

This function call increases the opacity of the specified element ID by 0.1 (10%) every 10 milliseconds. Note that after calling this function, execution continues in the current function (i.e. the fading starts in a new thread), so if you have code after the call to fade_object which depends on the fade, you'll have to delay it somehow.

It is just as easy to fade something out: you just need to set the fourth parameter in the call to fade_object to false; this decrements the element's opacity by the specified amount at the specified frequency, instead of incrementing it. Bear in mind that you'd have to set the element's opacity to 1 before fading it out, instead of setting it to 0.

Whilst calling the JavaScript in this manner is backwards-compatible, the title of the message is just "Message", and you cannot pass DOM snippets in — only plain text can be passed as the function parameter.

The two other functions are designed to retrieve the position (left and top, respectively) of a specified element on the page.

If I wanted to find the X-position (distance from the left-hand edge of the rendering area) of an element, I'd make the following function call:

var left_distance=findPosX(element);

I could do a similar thing to find the distance from the top of the rendering area:

var top_distance=findPosY(element);

Now for a more technical view of the functions:

Function declaration Description
public void fade_object(string fade_element_id,float add_opacity,int frequency,bool increasing_opacity) Fade the specified element in or out. This function doesn't return anything, and spawns a new thread for its execution.
public int findPosX(dom_element obj) Finds the distance from the left-hand edge of the rendering area of the specified element.
public int findPosX(dom_element obj) Finds the distance from the top edge of the rendering area of the specified element.

If you do use this script, please download it and host it with your website; do not leech my bandwidth! Additionally, please follow the terms of the license to the letter.

More updates

It seems that every other post is an update to the site, but that's how things are.

This time, I've gone so far as to move domains (my other host was getting on my nerves), and fix some of the issues surrounding the site (such as the comment thread display problem: a simple case of "replace NULL with 0").