http://www.chrispearson.org/pages/programming/javascript/wineshop02.asp
09h24
Friday, 21. November 2008

THE WINE SHOP

Contents

Introduction: The Wine Shop Site
Writing a cookie
Reading a cookie
Getting the document.cookie
Splitting out cookie value from document.cookie
Updating document.cookie
Deleting cookies
Conclusion

Back to page 1 of The Wine Shop article

 


The Wine Shop Site

The Wine Shop is a basic, five page web site which was developed to demonstrate how cookies can be used and how they can be created and manipulated by JavaScriptr code.

The site allows notional bottles of wine to be selected from two store departments, placed in an electronic shopping basket and notional orders placed for the notional wine.

The order processing, for reasons of practicality, updates the client side data but doesn't send the order back to the initiating web server. Sorry, you'll have to continue going down the off licence.

The code fragments in the text below are intended to be illustrative - See the code in the web pages themselves for complete, working scripts.

 

Top of this page

Writing a cookie

A cookie has the following format

cookieName=CookieValue; expires=expirationDate.toGMTString();path=sitePath;domain=siteDomain

Only the cookie name and cookie value pair is mandatory - The rest is optional. Here we will use the name/value pair and the expires/expiry date pair. The path and the domain pairs are documented elsewhere.

An example is seen in the checkout page (checkout.htm), where the user is required to enter values for name and address:

<form name="frmContact"
  method="post"
  action="">
<p>
 <font face="Verdana, Arial, Helvetica, sans-serif"
  size="2"
  color="#000000">
  First name
 </font><br>

 <input type="text" name="txtFirstName" cols="30">
 <Br>
 <font color="#000000"
  face="Verdana, Arial, Helvetica, sans-serif"
  size="2">
  Family name
 </font><Br
>
 <input type="text" name="txtSurname" cols="30">
 <Br>
 <font color="#000000"
  face="Verdana, Arial, Helvetica, sans-serif"
  size="2">
  Address
 </font><Br>

 <textarea name="txtAddress"
  rows="4"
  cols="30">
 </textarea>
</p>
</form>

When the Place Order link is clicked, a JavaScript function is invoked

     <a href="javascript:placeOrder();">Place order</a>

The function placeOrder() checks that the user has completed all the fields, then writes out their values to cookies:

function placeOrder() {
\\     There is some other code here in the web page . . .
     expireDate = new Date;
     expireDate.setMonth(expireDate.getMonth()+6);
     document.cookie = "firstName=" +
          escape(document.frmContact.txtFirstName.value) +
          ";expires=" +
          expireDate.toGMTString();
     document.cookie = "surName=" +
          escape(document.frmContact.txtSurname.value) +
          ";expires=" +
          expireDate.toGMTString();
     document.cookie = "Address=" +
          escape(document.frmContact.txtAddress.value) +
          ";expires=" +
          expireDate.toGMTString();

Notice here that, so long as the cookie's name is specified, followed by = then its value, the browser handles its addition to the cookies on file: document.cookie is always assigned the value. The expiry date is set at six months from now.

Technically the expiry date is optional but experience shows that, when the expiry date is omitted, odd things can happen - Cookies are deleted by the browser just when you start to need them - This may be the way the browser determines the validity of a memory-resident cookie: If no expiry is set then the cookie should be kept in memory for the duration of the current session, not written to a file and discarded when the session ends.

When the expiry date is reached, the cookie is said to be stale and the browser will delete it without reference to the user.

Notice also that the cookie name/cookie value pair is separated from the rest of the cookie by <space> ;

The delimiter for the rest of the cookie is just ;

This allows for some flexibility in accessing the components of a cookie when it is read back - See Reading cookies, below.

Note also that the process of writing a cookie is usually referred to as setting a cookie. A cookie can be set by script in a HTML page but it can't be set by the web server itself.

 

Top of this page

Reading cookies

Retrieving cookies is a matter of extracting the appropriate name/value pairs from document.cookie. Before we look at the examples in The Wine Shop here are some basics to put those examples into context. Notice, again, that all through these examples, the browser determines which cookies we can access - There is nothing the scripts (or the server) can do to determine which stored cookies are made available. This makes cookies very secure, both for the user and for the web site wanting to store data on the client's machine.

At its most straightforward you can insert the following line into a script

     alert(document.cookie);

which will display the entire cookie string (as shown below). To display all the currently-accessible cookies on a web page, try the following code in a HTML page and see its sample output.

<body bgcolor="#FFFFFF" text="#000000">
Cookie contents<BR>
<SCRIPT LANGUAGE="JavaScript">
     if (document.cookie == "") {
          document.write("<BR>There are no cookies to display");
     } else {
          var thisCookie = document.cookie.split("; ");
          document.write("<BR>Raw cookie data<BR>");
//   Show the cookies as they are stored, together with escape sequences

          for (i=0; i<thisCookie.length; i++) {
               document.write("<BR>Cookie named " +
                    thisCookie[i].split("=")[0]);
               document.write(" has a value of " +
                    thisCookie[i]Split("=")[1]);
          }
          document.write("<BR><BR>Unescaped cookie data<BR>");
//   Show the cookies restored to plain text, removing escape sequences
          for (i=0; i<thisCookie.length; i++) {
               document.write("<BR>Cookie named " +
                    unescape(thisCookie[i]Split("=")[0]));
               document.write(" has a value of " +
                    unescape(thisCookie[i]Split("=")[1]));
          }
     }
</SCRIPT>
</body

If the user had entered

  Angus for the first name,
  McNutter for the family name

and

  3, Ironside Villas, Scunthorpe for the address, as shown to the right, the alert message would display as below:

While the second script would output the following in the web page

The alert(document.cookie) function call displays the cookie data without unescaping its contents, displaying spaces as %20, for instance.

In The Wine Shop the cookie data is read back to display the user's name on the home page (default.htm), in code incorporating a check to determine whether there is stored data to use:

     <SCRIPT LANGUAGE="JavaScript">
          var thisCookie = document.cookie.split("; ");
          var userName = "";
// Get user's name
          for (i=0; i<thisCookie.length;i++) {
// Check the name of each cookie returned
               if (thisCookie[i]Split("=")[0] == "firstName") {
// Assign cookie value to field when its name is found
                    userName = unescape(thisCookie[i]Split("=")[1]);
               }
          }
     </SCRIPT>

Followed later by this code which embeds the greeting, if available,into the page

     <SCRIPT LANGUAGE="JavaScript">
          if (userName != "") {
               document.write("Hello " + userName + "<BR>");
          }
     </SCRIPT>

As in all the cookie-retrieval code, we need to split the cookie string into an array on the delimiter ;<space> using document.cookie.split("; "), then loop through the cookies looking for the cookie name in which we're interested. Once the cookie is located in the array, we can split the name element and its associated value by splitting the pair into a two-element array using thisCookie[i]Split("="), accessing the name element in thisCookie[i]Split("=")[0] and the cookie value in thisCookie[i]Split("=")[1]. Because = is one of the characters which is escaped in text, it only appears in between the cookie elements. That is, a cookie called TestCookie with the content specified as Two=Parts would store the data value Two%3Dparts, the whole cookie being

     TestCookie=Two%3Dparts;

%3D being the escape sequence for the equals sign, =.

 

Top of this page

Getting the document.cookie

Cookies may be stored -by default in Windows 2000, using Internet Explorer 5.x, for instance - as text files in the folder

     C:\Documents and Settings\<user name>\Cookies

where <user name> is the user's NT login id, in my own case the folder would be

     C:\Documents and Settings\CPearson\Cookies

Using Netscape Communicator under Windows 9x, cookies are saved in the folder

     C:\Program Files\Netscape\Users\<user name>

If you can locate some cookies and take a look at them (double click to open them, probably associated with Notepad in Windows as they are .TXT files) you will see the name/data pairs together with masses of information on date and time and the originating web server.

All of this allows the browser to manage access to cookies and their security.

The way they are stored, referencing the user's name, also retains security between users on the same computer.

 

But knowing any of this doesn't enable you, as a script coder, to get at cookies which don't belong to you. Getting access to the cookies inside your web server's security fence is simply a matter of using the existing contents of the document.cookie, as we did above:

     alert(document.cookie);

and by assigning a value to the cookie:

     document.cookie = "cookieName" + "=" + "cookieValue" + "; ";

Remember, also, that the user can opt to reject cookies or to choose to detect each cookie access request and choose whether to allow that action or not. If you're designing a site that uses cookies then bear this in mind. If the user rejects a cookie you probably won't want to continue challenging the browser to write subsequent cookies.

Summary: The browser manages access to cookies through document.cookie.

 

Top of this page

Splitting out cookie values from document.cookie

Name/value pairs can be extracted from a cookie record by using the split command, specifying a <semicolon><space> delimiter:

     var cookieArray = document.cookie.split("; ");

which places the cookie elements into an array, starting at thisCookie[0], through thisCookie[1] up to the index thisCookie.length - 1 (since the array starts at zero, the last array element is indexed one less than the number of elements)

Since the name is separated from its associated value by an equals sign, the same technique can be used to extract the name and the value

     var cookieName = cookieArray[0]Split[0];

which gets the name from the first element of the two-element array and

     var cookieValue = cookieArray[0]Split("=")[1];

where [1] indexes the second element in the array: the value element.

 

Top of this page

Updating document.cookie

Updating a cookie requires exactly the same script code as writing it in the first instance. Since all cookie information is saved by reference to its name (within the server/user definition handled by the browser) updating from

     document.cookie = "orderValue" + "=" + "22.45" + "; ";

to an order value of 35.55 is a case of rewriting the cookie with the new value

     document.cookie = "orderValue" + "=" + "35.55" + "; ";

 

Top of this page

Deleting cookies

Cookies become stale when their expiry date is reached. If a stale cookie is read by a browser it is deleted - without a request for confirmation - and treated as though it did not exist. If cookies are not accessed it is possible for them to remain physically on file beyond their expiry date, even though they are inaccessible.

Deleting a cookie is achieved by setting its expiry date to something before now.

In The Wine Shop the shopping basket cookies are cleared by deleting them while the user's name and address cookies are retained. The user's details are given an expiry of six months - This was an arbitrary decision providing a long-enough shelf life with expiry within a reasonable time frame.

The cookies for the shopping basket are created in the shopping pages (reds.htm and whites.htm) using this function

     <SCRIPT LANGUAGE="JavaScript">
     function addWine(wineType, winePrice) {
//   More code here in actual script . . .
          expireDate = new Date;
          expireDate.setMonth(expireDate.getMonth() +6 );
          document.cookie = escape(wineType) +
               "=" +
               winePrice +
               ";expires=" +
                expireDate.toGMTString();

which is passed the name of the wine and its price from the hyperlink

     <a href="javascript:addWine('Wine 1', '2.99');">
        Wine 1 &pound;2.99
     </a>

It is cleared by setting its expiry date to a moment before now in this function in checkout.htm:

     function placeOrder() {
//   More code here in actual script . . .
//   Create an expiry date in the past
          deleteExpiry = new Date;
          deleteExpiry.setDate(deleteExpiry.getDate()-1);
//   More code here in actual script . . .
//   Must be a wine type cookie
          cookieName = unescape(thisCookie[i]Split("=")[0]);
          cookieValue = unescape(thisCookie[i]Split("=")[1]);
          document.cookie = escape(cookieName) +
               "=" +
               escape(cookieValue) +
               ";expires=" +
               deleteExpiry.toGMTString();

The next time a browser sees any of these cookies it will delete them.

 

Top of this page

Conclusion

The Wine Shop was designed and implemented to demonstrate how cookies can be used to solve a common requirement - getting data to persist between web pages without posting data back to a server - and, within that context, how to

  • write cookies
  • read cookies
  • update cookies
  • delete cookies

by discussing the JavaScript code which manipulates them.

Some long time ago, Netscape published a fairly definitive article, Persistent Client Side HTTP Cookies, which said that cookies

are a general mechanism which server side connections such as CGI scripts can use to both store and retrieve information on the client side of the connection

The important security aspect of how these state objects, as the article defines them, cookies as they are now generally known, is that the information stored on the client side contains the initiating server's identity and the accepting user's identity, making the exchange of the information - passing the cookie - an intimate transaction between, on one side, the user and the user's browser and, on the other side, the initiating web server and the page it has served.

Cookies have a bad press, mostly down to completely unfounded security fears, which they shouldn't have. Their main shortcoming is down to users' distrust and users' propensity to disable cookie processing by their browsers. To use cookies effectively, you must check that they will be accepted by the client and will be available when script attempts to access them. If disabled, you must switch the cookie functionality to server-side processing.

Top of this page
   

xxx,xxx

copyright ©2000 - 2008 Chris Pearson