Friday, December 30, 2005

Incredible Appearing AJAX Map

Everyone's heard of the the big AJAX projects like Google Maps and Yahoo's Beta Mail program, but if you're just wanting to get into AJAX, those sorts of projects are probably a bit daunting. Here's something simpler. (Not quite tutorial simple because I get bored too easily, but still, the good kind of simple.)

Check out this site and click on the street address. If your internet connection isn't too fast, you'll get a loading message and then a nifty little popup map with buttons on it including a close button that will make it go away. How's this work? It centers around this little bit of the page:

<a class="fakelink" onclick="showMap();">6701 ...</a>
<div id="mapWindow">
</div>
</p>



A little bit later, we're going to look at the code that adds in the map window, but as you've probably already guessed, it simply uses javascript to pop in the extract code inside of the division entitled "mapWindow". The other thing that probably jumps out is the "fakelink" class of the address. That class corresponds to an entry in the page's css file where I define the common attributes of text necessary to fake a hyperlink so as not to confuse the site's users. It is defined as follows:

.fakelink{text-decoration:underline;cursor:pointer;cursor:hand}

Ok, now the fun stuff. What happens when you actually click on the link? According to the onClick attribute, we call a function called showMap(). This is a javaScript function that appears as follows:

function showMap() {
var url = "mapwindow.html";
document.getElementById('mapWindow').innerHTML = "Loading Map...";
http.open("GET", url, true);
http.onreadystatechange = handleHTTPResponse;
http.send(null);
}


In the second line there, we see the reason for the "Loading Map..." message. As soon as you click the button, the javascript searches through the document (an object representing the structure of the current web page) until it finds an element defined by the id "mapWindow" which you will recall was what we named the empty division that awaited the loaded map. Next, we employ an http object. This is actually an XMLHTTPRequest object created earlier
through the following code on the index page:

var http = getHTTPObject();

function getHTTPObject() {
var xmlhttp;

if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
try {
xmlhttp = new XMLHttpRequest();
} catch (e) {
xmlhttp = false;
}
}
return xmlhttp;
}


This getHTTPObject() is actually a generic function I stick in a file I call ajax.js whose sole purpose is to contain it. If someone actually starts making me support pitifully old browsers, I will simply update this function appropriately to return the correct object. Consider that a hint.

Now, back to the code in showMap().
First, we make a request to "Open" the URL. The first parameter to open is either GET or POST, but since this is simple and because we actually won't be passing any parameters, we will use GET. The second is, of course, the URL of the page where the request is to be made. The next thing that we need to set up in the http object is the callback function which will be informed when something has happened this request (like it being done and loaded). We call this one handleHTTPResponse, and then finally send the request.

function handleHTTPResponse() {
if (http.readyState == 4) {
document.getElementById('mapWindow').innerHTML = http.responseText;
}
}


When the page is finally loaded, our handleHTTPResponse function is called. We check the ready state for state 4 which indicates that the request had completed. The response comes back in our XMLHTTPRequest object in the responseText field. If we were going to be parsing it, we could parse this variable via standard text processing or XML processing. In this case though, we just want to display it, so we pop in there to the same place where we put our loading message earlier, thus simultaneously removing the loading message and displaying the map.

The only thing left is what happens when you press the close button. The onClick for the close button contains a call to killMap() which merely removes the innerHTML from the page.

function killMap() {
document.getElementById('mapWindow').innerHTML = "";
}


This may seem inefficient because it basically means that a user who repetitively click on the open/close map window links will cause the web server to have to repeatedly serve up the map window page over and over again. However, since it is a simple HTML document, the user's browser will in all probability simply return the locally cached copy. For the rare circumstance when users do not locally cache, it would be more effecient to simply hide and re-display the window, but that is left as an exercise for the reader. :)