Chapter 19

Shopping Cart


CONTENTS

If you've got an online catalog and your visitors browse the "shelves" of your store, it would be nice to give them a place to put the products they're going to buy- a CyberShopping Cart.

Off the Shelf

The shopping cart is an extension of the online catalog, providing a means to "remember" which products the user has selected for purchase as they move from page to page. This implies that the cart is an integral part of the catalog, and how you design your catalog controls how you implement your cart.

TIP
One good example of a shopping cart in action is Netscape's General Store: http://merchant.netscape.com/netstore/index.html. You can purchase copies of their browsers, servers, books, and even clothing.

Whether you use client- or server-side scripting to implement the shopping cart, you will probably interface the cart to the catalog through a form:

<FORM METHOD=POST ACTION="/cgi-bin/cart.cgi">
   ...
   <INPUT TYPE=SUBMIT VALUE="I'll take it">
</FORM>

You can either intercept and process the shopping request from within the browser (client-side scripting), or pass the data on to the server for processing (server-side).

Server-Side Cart

To implement a shopping cart from the server, you'll need to create a temporary database file that stores the user's selections. This is easily done by having the cart.cgi script generate the standard flat-file database sequence:

  1. Open a file, if it exists; otherwise, create one.
  2. Read any data from the file into an internal list.
  3. Add the new purchase to the list.
  4. Write the whole thing back out again.

The only trick is making sure that the file name you pick isn't used by anyone else. A common technique for this is to keep a separate counter file on the server that records the last order number used. When the cart first loads, the counter file is checked, incremented, and the new number is used as the new cart file name. To make certain that this happens at the correct time, create the cart file when the user first enters the catalog as part of the catalog.cgi script.

Additionally, as each catalog page generates, it's necessary to include information back to the browser identifying the name of the cart file. This is done in one of two ways:

  1. As a parameter added to the ACTION attribute of the form cart.cgi generates.
  2. As a hidden field contained within the form.

The hidden field is a bit less obtrusive, but either method works.

Additional information you may want to pass back to the browser may include:

The total number of items selected.
The total cost of all selected items.

A side-effect of using server-side temporary files is the potential for the user to "wander off" your site if they decide not to continue shopping, or to be disconnected from their provider due to a bad modem connection, or by simply hanging up the phone. This results in a temporary file lying around on your system, which can be handled in one of two ways:

  1. You must periodically do some system maintenance to clean up any files left over.
  2. Switch to storing the shopping cart on the client computer within the user's browser, using cookies.

Maintenance can be automated by adding some additional functions to your server's periodic maintenance routines that run. Have it scan the cart directory and delete any old files. Client-side scripting of the cart eliminates this problem entirely, and is discussed next.

Client-Side Cart

The client-side (browser) cart relies on the browser's ability to dynamically create objects and add data to the local dataset within the browser's dataspace.

In order for this to work, you need to play a couple games with how pages are loaded in the browser. Because loading a new page clears any existing JavaScript objects (functions, methods, and so on) from memory, you need to create a fake shell document that contains the actual data.

Object Persistence in JavaScript

Because loading a new page totally clears the browser of any JavaScript (objects, functions, methods, and so on) that was associated with that page, storing the cart data within the document that contains the shopping form won't work. If your site is framed, however, you can store the cart objects in a frame that doesn't get reloaded. This keeps its persistence even though the catalog display pages and order form are constantly being updated.

It may not be practical to dedicate a frame simply to hold data, especially when you want to use as much of the browser as you can for your products. Dedicating a frame has, however, been demonstrated in rather unique ways on some sites.

The parent frame is the document that contains the <FRAMESET> directives that structure your site. If you store your code in this frame, you can access the objects as often as you want until the user leaves your site. A template for a parent frame document that stores any objects you want is shown in listing 19.1.

TIP
For an example of centralizing JavaScript code and data within a parent frame that the child frames then use, check out the home site for The Complete Idiot's Guide to JavaScript, published by Que at: http://www.visi.com/~sjwalter/javascript/.


Listing 19.1  Storing Data in the Parent Frame
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!-- begin hide
...
your code can go here
...
// end hide -->
</SCRIPT>

<FRAMESET ...>
   ...
</FRAMESET>

With this used as the parent document, any pages loaded within child frames reaches back and accesses any functions or objects by simply prefacing any references to the desired JavaScript component with parent., as follows:

parent.objectOrFunction

This is, of course, assuming that the document making the call is a direct child of the parent. A direct child is loaded within the frames defined by the <FRAME> tags in the parent. If any of the children are themselves framesets with their own children, you have to preface the object call with another parent.:

parent.parent.objectOrFunction

This process continues for every level deeper you get into the frame structure-although at some point, it becomes impractical to nest frames too deeply. If you're not certain how deep you're getting in your frames, you can always access your objects from the top instead of the bottom. The JavaScript top property is a synonym for the topmost window within the browser. Because your parent document is loaded first, it is the topmost window, meaning that you can access objects within it by:

top.objectOrFunction

which will work properly no matter what child (or child of a child) window you're in within the site.

CAUTION
Unfortunately, it's not necessarily safe to assume that your parent document will always be the top-most document. If your site is popular, other sites may include links to yours, and if one of those sites itself uses frames and the Web master forgets to include a TARGET="_top" attribute in the link to your site, your shopping cart may open up within one of his child windows-in which case, any references to top will reach into his pages.
Possible workarounds for this problem are:
  • Set up your shopping section as a link from another higher (home) page and be sure to include the TARGET attribute.
  • Have your catalog open a whole new browser window.
  • E-mail every Web master with a link to your site and ask them to fix their pages.
Naturally, the first of these three is probably the best. While it may not cure all possible problems, it should solve most of them.

Now that this fake frame is created, all of your JavaScript methods can be placed in index.htm and referenced from any document within the frame tree using the special JavaScript reserved word top. This will access the topmost window (or frame) of a tree.

There is one last point to consider when using this technique. If you are planning to store any functions within your parent frame, and those functions do manipulation on the window of the current (child) page, you'll need to pass an additional parameter to the functions-the window on which you will be working. Without this, JavaScript can't correctly resolve properties of the specific window object. From within the parent document, any reference to this has an entirely different meaning. Also, the minute you start to reach from one frame into the next, self is no longer guaranteed to work correctly, and you need to pass window to force the correct resolution.

User-Defined Objects

Once you know where you're going to store your objects, you need to create them. This requires creating a user-defined object.

To create your own objects, you first need to write a function whose name is the same as that of the object you wish to define (like Item). You then initialize the properties of the object, such as its name and price. Listing 19.2 is an example of the object-creation function.


Listing 19.2  Defining a Cart Object
function Item(strDescription, strCost) {
   this.Name = strDescription;
   this.Cost = strCost;
   return this;
}

With your object type defined, you simply create an array (the "cart") that stores another item each time the user clicks a button. This is demonstrated in listing 19.3.


Listing 19.3  The Cart
<SCRIPT LANGUAGE="JavaScript">
<!-- begin hide
...
cart = MakeArray();
numItems = 0;
totalCost = 0;

function PickOneUp() {
   cart[++numItems] = new Item(theForm.Name.value, 
                               theForm.Cost.value);

   totalCost += parseFloat(theForm.Cost.value);
}
// end script -->
</SCRIPT>
...
<FORM METHOD=POST ACTION="/cgi-bin/purchase.cgi">
   ...
   <INPUT TYPE=BUTTON VALUE="Pick one Up" ONCLICK="PickOneUp()">
</FORM>

CAUTION
When indexing into a complex array-one that holds objects, not just simple properties or strings-it's critical that your indexes start with 1 instead of 0. While some built-in JavaScript arrays start their indexing at 0, this won't work for user-defined objects-it tends to scramble the data and makes it difficult to manipulate later.

This simple loop structure keeps the total count of objects in numItems, and the total cost of the cart in totalCost. Note that when adding to the value stored in totalCost, it's a good idea to use the parseFloat() method to ensure that the value retrieved from the Cost field of the form is treated as a number. JavaScript tends to be fond of strings and assuming that it will treat a number as a number is not a good idea.

From Here…

Chapter 17, "Creating Online Catalogs," introduces a method for displaying your wares on the Web. This chapter discusses extending the catalog system by allowing your shoppers to store items in a cart as they peruse your shelves. For more information on the various tricks and techniques used here, check out: