|
Introduction
When building Web sites of even modest complexity, one must determine an effective way to maintain state. If the user's browser supports cookies, you can use session variables. However, if cookies are not available or if you don't want the overhead of using cookies, you can maintain almost any kind of data using hidden frames. By using the ubiquitous shopping cart scenario, we will explore simple ways to read and write data, and call functions from a hidden frame.
Frames
To create a hidden frame, you simply set a frame to fill 100 percent of the browser and set the hidden frame to fill the rest (i.e., 0 percent). Here, we create two frame rows and divide the top row into two columns (the bottom row is the hidden frame):
<frameset rows="100%,*" framespacing="0">
<frameset cols="50%,50%" framespacing="0">
<frame name="productsFrame" src=" spacer.asp" scrolling="auto">
<frame name="cartFrame" src="spacer.asp" scrolling="auto">
</frameset>
<frame name="hiddenFrame" src="cartData.asp">
</frameset>
A quick note about frames:- you should always load a page into each frame otherwise some browsers (like Netscape) will load a page of their own, causing uncertain results. Often I just use a blank page, like spacer.asp, for this.
Let’s look at each of the pages in this Web:
cartData.asp is really the brains of this operation. All of the data and many of the functions are located here and can be accessed from any other page that is loaded in one of the upper frames. In our example, we will construct two arrays - one to store the products and one to store the cart items. In the real world, you would probably query a database and generate the arrays from that. However, to keep things simple, and to allow the examples to function without having to create a DSN etc., we create the arrays statically. First, we construct ourProductArray:
var numberOfProducts = 4;
//create a new array
var ourProductArray = new Array(numberOfProducts + 1);
//this element holds the array size
ourProductArray[0] = numberOfProducts + 1;
// now, populate the array with products
ourProductArray[1] = new item(1,'Player Piano',1500)
ourProductArray[2] = new item(2,'Trombone',400)
ourProductArray[3] = new item(2,'Clarinet',350)
ourProductArray[4] = new item(2,'Ukulele',300)
Then, we initialize ourCartArray, which will hold the cart data:
var ourCartArray = new Array(1);
ourCartArray[0] = 0; // set the size to 0 (number of cart items).
Technically, theCartArray[0] should be set to 1, but it makes life a easier if we start with 0. Of course, both arrays are declared outside of any function, making them global so they will persist after the page is loaded.
Finally, because both products.asp and showCarts.asp are dependent on arrays set up in cartData.asp, we load those pages after having generated the arrays:
parent.productsFrame.location = "products.asp";
parent.cartFrame.location = "showCarts.asp";
showCarts.asp displays the data in ourCartArray. That’s basically all it does! The code to draw the cart table is contained in the drawCart() function. This is the first time we use data that is found in the hidden frame, so take a look at the reference to ourCartArray where we initialize numCartItems. The notation can be confusing, but you can reference any object in any frame this way.
Now, we just go through each item in the array and, using JavaScript, place the data in a table. Just for fun, we also keep a running tally of item costs, so we can show a total at the end.
function drawCart() {
var numCartItems = parent.hiddenFrame.ourCartArray[0]; // get number of cart items
if ( numCartItems > 0){ // check if there's anything in the cart
document.write("<form name='cartform' method='post'>");
document.write("<table width='90%' border='1'>" &_
"<tr><th>Item #</th>" &_
"<th>Description</th><th>" &_
"Price</th></tr>"); // setup the table
var ItemID,total = 0;
for (index = 1;index <= numCartItems;index++) {
ItemID = parent.hiddenFrame.ourCartArray[index].ItemID;
document.write("<tr><td align='center'>");
document.write(ItemID);
document.write("</td><td align='center'>");
document.write(parent.hiddenFrame.ourProductArray[ItemID].Description);
document.write("</td><td align='center'>");
document.write("$" + parent.hiddenFrame.ourProductArray[ItemID].Price);
total += parent.hiddenFrame.ourProductArray[ItemID].Price;
document.write("</td>");
}
document.write("<tr><td colspan='3'><br></td></tr>");
document.write("<tr><td><br></td><th>Total:</th><th>$" &_
+ total + "</th></tr>");
document.write("<tr><td colspan='3' align='center'><input type='button' value='Empty Cart'");
document.write(" onClick='emptyCart()'></td></tr>");
document.write("</table>"); // close the table
document.write("</form>"); // close the form
}else { // there's nothing in the cart yet.
document.write("<font color='red'>Your cart is currently empty.</font>");
}
}
The other function found in this page is emptyCart(). This function reloads cartData.asp, which reinitializes both of our arrays, and then reloads the products and carts pages. Later, I’ll talk about some other issues surrounding this reset process, but for now, you can see how the whole Web revolves around the data in the hidden frame.
products.asp, the last page we need to examine, uses ourProductArray to fill in a table from which we can add items to our cart. When the addToCart() function is called after a button click, ourCartArray is expanded by one element, and that element is filled with an ItemID and a quantity, which is all we need because we’ve cleverly made sure that the ItemID is equal to the element number of the products array. Then we just reload showCarts.asp, which uses the updated array as the basis for its cart table.
Summary
The visible frames can be loaded with virtually anything, and the hidden frame will always be there, holding data until it’s needed. You can maintain state easily and reliably.
Some other uses of storing data in this way are:
- Record the time of the last database update to prevent multiple form submissions;
- Save the authentication time by writing a timestamp into a hidden field which can be referenced whenever an authentication check is required;
- Save any SQL string that was used in a database action. If there’s an error, you can go back and easily re-create the state before the error.
About the Author
Mark Burnham is President of Manhattan Heavy Industries, Inc., a
New York City based Internet development company.
|