- 2.1 Not a Mockup
- 2.2 A Technique Without a Name
- 2.3 What Is Ajax?
- 2.4 An Ajax Encounter of the First Kind
- 2.5 An Ajax Encounter of the Second Kind
- 2.6 An Ajax Encounter of the Third Kind
- 2.7 The Shape of Things to Come
- 2.8 Summary
2.4 An Ajax Encounter of the First Kind
Now that I've gushed about the why of this technique, let me offer a little insight on the how of this technique. Let's start with the three HTML documents shown in Listing 2-1, Listing 2-2, and Listing 2-3. Some readers might not consider this a true example of Ajax, but it does share many of the same qualities of Ajax, in much the same way that a Star Trek fan and a Star Wars fan share many of the same qualities.
Listing 2-1. HTMLfs.htm
<html> <head> <title>HTMLfs</title> </head> <frameset rows="100%,*"> <frame name="visible_frame" src="visible.htm"> <frame name="hidden_frame" src="hidden.htm"> <noframes>Frames are required to use this Web site.</noframes> </frameset> </html>
Listing 2-2. visible.htm
<html> <head> <title>visible</title> <script language="javascript"> /* Perform page initialization. */ function initialize() { } /* Handle form visible form onchange events. Values from the visible form are copied to the hidden form. */ function changeEvent(obj) { parent.frames[1].document.getElementById(obj.id).value = obj.value; } /* Submits the form in the hidden frame then reloads the hidden frame. */ function submitForm() { parent.frames[1].document.getElementById('hidden_form').submit(); parent.frames[1].document.location = "hidden.htm"; } </script> </head> <body onload="initialize()"> <form name="visible_form" id="visible_form"></form> </body> </html>
Listing 2-3. hidden.htm
<html> <head> <title>hidden</title> <script language="javascript"> var reBrowser = new RegExp('internet explorer','gi'); /* Perform page initialization, waits for the visible frame to load and clones the hidden form to the visible form. */ function initialize() { var hiddenForm = document.getElementById('hidden_form'); if(reBrowser.test(navigator.appName)) { while(parent.document.frames.item(0).document.readyState != 'complete') { } parent.frames[0].document.getElementById('visible_form').innerHTML = hiddenForm.innerHTML; } else { var complete = false; while(!complete) { try { parent.frames[0].document.getElementById('visible_form').appendChild (hiddenForm.cloneNode(true)); complete = true; } catch(e) { } } } } </script> </head> <body onload="initialize()"> <form name="hidden_form" id="hidden_form" action="post.aspx"> <h1>Address Information</h1> <table border="0" width="100%"> <tr> <th width="30%" align="right">Name: </th> <td align="left"> <input type="text" name="name" id="name" value="" onchange="changeEvent(this)"> </td> </tr> <tr> <th align="right">Address Line 1: </th> <td align="left"> <input type="text" name="address1" id="address1" value="" onchange="changeEvent(this)"> </td> </tr> <tr> <th align="right">Address Line 2: </th> <td align="left"> <input type="text" name="address2" id="address2" value="" onchange="changeEvent(this)"> </td> </tr> <tr> <th align="right">City: </th> <td align="left"> <input type="text" name="city" id="city" value="" onchange="changeEvent(this)"> </td> </tr> <tr> <th align="right">State: </th> <td align="left"> <input type="text" name="state" id="state" value="" onchange="changeEvent(this)"> </td> </tr> <tr> <th align="right">Zip Code: </th> <td align="left"> <input type="text" name="zip" id="zip" value="" onchange="changeEvent(this)"> </td> </tr> </table> <br> <input type="button" value="Submit" onclick="submitForm()"> </form> </body> </html>
2.4.1 A World Unseen
Any developer familiar with the use of frames and framesets will find Listing 2-1 pretty normal looking. However, one item isn't plain vanilla: the rows="100%,*" attribute on the frameset element, which states that the first frame gets 100 percent of available rows. The asterisk (*) states that anything left over goes to the second frame. In this example, there is nothing left over, so it is the equivalent of coding zero. This results in the first frame being visible and the second frame being hidden. In essence, this is a sneaky way to hide what's going on from prying eyes—namely, the user. The next two listings are the visible frame, Listing 2-2, and the hidden frame, Listing 2-3. Listing 2-3 is where the real mad science happens.
2.4.2 Enter JavaScript
Listing 2-2 is short and sweet, basically two short JavaScript functions that don't appear to do anything. The first of these functions, changeEvent, is just what it says it is, a handler for an on change event. When fired, it copies the value associated with the current object on the current frame to one with the same ID on the hidden frame. The second function, submitForm, submits a form; however, like the previous function, it works with the hidden frame by locating and submitting the form there.
This leaves just one question: Where does the HTML for the visible form come from? The answer lies in Listing 2-3, the one for the hidden frame. Like the visible frame, it has JavaScript functions and a form. There is, however, a major difference in the form. Unlike its visible counterpart, it has all of the HTML necessary to make a nice little form. The trick is getting it from the hidden frame to the visible frame.
This magic is accomplished in the pages' on load event handler, initialize. This function waits for the other frame to load and then copies this form's inner HTML to the other frame. When this is done, the result is the normal-looking web page shown in Figure 2-1. The way it behaves, however, is almost application-like, with parts of the visible page being updated each time the hidden frame does an unload/reload cycle.
Figure 2-1 A normal-looking web page that functions almost like a desktop application