Previous Page TOC Next Page See Page



- 9 -
Using Cookies


One of the limitations of standard hypertext transfer protocol and the common gateway interface mechanism is that the combination of the two provides no way to maintain client-state. When developing large, multi-page applications on the Web or for sites that need to maintain user information between user sessions it is necessary to use complex CGI-BIN scripts and server-side storage to maintain state information.

A simpler solution is offered in Cookies—a system for maintaining client state available in Netscape Navigator and Microsoft Internet Explorer.

Using JavaScript, it is possible to create and use Cookies to maintain client-side state throughout a Web-based application.

This chapter covers the following three topics:


Cookie Basics


The Cookies mechanism is not a unique feature of JavaScript. Instead, the Cookie definition has been designed to work in an environment of standard HTTP and CGI-BIN.

The basic model for Cookies follows:

  1. When the server sends a document to the user, an additional entry is added to the HTTP header, instructing the browser to store data in a Cookie.

  2. The browser stores the data in the Cookie.

  3. Later, if the user requests a document with an associated Cookie, the request is sent with an additional item in the header reflecting the content of the Cookie or Cookies associated with that page.

Cookies have the following features:

Using this model, Web sites can track user information between pages in a session and between sessions by storing information in Cookies. The information can then be processed when the user requests a page and a custom page can be returned to the user based on the content of the Cookie. This enables site developers to avoid other methods of maintaining state across multiple pages in a site, such as forms with numerous hidden fields.

How Cookies Work


Cookies add two header entries to HTTP requests to the server and responses from the server: cookie and Set-Cookie.

HTTP header entries take the form of name-value pairs specified with

Field-name: Data

These entries are used to communicate information between the client and server, such as the MIME type of the document being sent by the sever. A MIME type header entry might look like

Type: text/html

or

Type: image/gif

The Set-Cookie Header Field

When the server sends a document to the browser with which a Cookie needs to be set, the server adds a Set-Cookie header entry to the HTTP header sent with the document.

The Set-Cookie entry takes the following form:

Set-Cookie: name=VALUE; expires=DATE; path=PATH; domain=DOMAIN; secure

The name=VALUE pair is a required part of the Set-Cookie field. All other entries are optional. Table 9.1 describes the attributes of the Set-Cookie field.

Table 9.1. Attributes of the Set-Cookie header field.

Attribute Description
name=VALUE Specifies the name of the Cookie and the content to be stored in the Cookie. VALUE is a string that can't contain semi-colons, commas or spaces.
expires=DATE Specifies the expiry date for the Cookie. The client will stop storing the Cookie after the date specified by the expires attribute. DATE takes the form Wdy, DD-Mon-YY HH:MM:SS GMT—all dates are stored in Greenwich Mean Time. The default value for expires is the end of the current Navigator session.
path=PATH Specifies the path portion of URLs which the Cookie is associated with. A match of both the path and domain attributes with a requested URL means the Cookie is sent to the server along with a document request. The default value for the path attribute is the path of the document that set the Cookie.
domain=DOMAIN Specifies the domain portion of URLs with which the Cookie is associated. The default value for the domain attribute is the domain of the document that set the Cookie.
secure Specifies that the Cookie should only be transmitted across secure links using the SSL protocol.

Multiple Set-Cookie fields can be included in a response header from a server. It is important to remember that Cookies with the same path, domain,and name attributes overwrite previously created Cookies with the same specifications.



The ability to overwrite previously set Cookies provides a mechanism for deleting Cookies by writing a new one with an expiry date in the past.


The Cookie Header Field

The Cookie header field is used by the browser to include Cookies with document requests. It takes the form of a list of name=VALUE pairs of Cookies associated with the document being requested

Cookie: name1=VALUE1; name2=VALUE2; name3=VALUE3 ...

Limitations of Cookies


Navigator limits the number of Cookies it will store to a total of 300. Each Cookie has a four kilobyte size limit, including all the optional attributes sent in the Set-Cookie header field.

In addition, any single domain can only store 20 Cookies. This ensures that one site cannot dominate the Cookie mechanism of a browser by flooding it with Set-Cookie header fields. When either limit is reached, the browser deletes the least recently used Cookie to make room for the new Cookie. If a Cookie exceeds four kilobytes, it is trimmed to fit.

Application of Cookies


Cookies are used today on the Web for a number of different applications:

Figure 9.1. Multi-lingual sites can store a user's preferred language in a Cookie.

Figure 9.2. The next time a user accesses the site, it will come up in the selected language rather than the default language.

The cookie Property


Although the Cookie mechanism in itself provides a means to store state information with the client, it still requires the use of CGI-BIN scripting and other server-side processing in order to take advantage of them.

JavaScript provides the cookie object so that it is possible to create and use Cookies entirely at the client-end without any need for server processing or interaction.

The cookie property of the document object is the means by which scripts can work with the Cookies. It exposes all the attributes of the Cookies associated with a document to the script and allows scripts to set Cookies.

The property itself is simply a string with the value that would be sent out in a Cookie field in the request header for the page in question. Being a string, it can be manipulated like any string object using all the properties and methods of the string object, such as length, charAt(), and indexOf().

Setting Cookies in JavaScript


Cookies are set in JavaScript by assigning values to document.cookie. Unlike regular strings, however, assigning a value to the property does not overwrite existing Cookies. Instead, it creates a new Cookie for the document.

The value assigned to document.cookie should be a string of the same format as is sent in a Set-Cookie field in a response header from the server:

document.cookie = "cookie1=First_Cookie";

The preceding should create a Cookie named cookie1 with a value "First_Cookie" that will expire at the end of the current Navigator session. Similarly,

document.cookie = "cookie2=Second_Cookie; expires=Mon, 01-Jul-97 12:00:00 GMT; path=/";

should create a Cookie named Cookie2 which expires on July 1, 1997, and which is valid for all documents in the domain of the current document.

If these two Cookies had been set in the same document, then using document.write(document.cookie) should produce results like the following:

cookie1=First_Cookie; cookie2=Second_Cookie;

Specifying Expiry Dates

Although it is possible to explicitly set expiry dates by building a string of the form

Wdy, DD-Mon-YY HH:MM:SS GMT

it is easier to user the Date object for creating dates.

For instance, to create an expiry date one year from the current date and then set a Cookie with that expiry date, the following code could be used:

var expires = new Date();
expires.setTime (expires.getTime() + 24*60*60*365*1000);
document.cookie = "cookie3=Third_Cookie; expires=" + expires.toGMTString();

Here, an instance of the Date object is created for the current date, it is reset to one year in the future by adding the number of milliseconds in a year to the current date, and then it is output as a string in GMT as needed for the Cookie's expires attributes.

Encoding Values for Cookies

The values stored in a Cookie are limited by the fact that they cannot contain spaces, semi-colons or commas. This means that any string value cannot just be assigned directly to a Cookie.

Instead, it is necessary to use some type of encoding algorithm on a string before it is stored in a Cookie, and then a decoding algorithm needs to be applied when the value is read back from the Cookie.

JavaScript provides the built-in escape() and unescape() methods. escape() encodes a string using the scheme used in URL strings. unescape() reverses the process. In the preceding example, where "Third_Cookie" was stored in a Cookie, the string "Third Cookie" could have been encoded using escape() instead:

var expires = new Date();

expires.setTime (expires.getTime() + 24*60*60*365*1000);
var value="Third Cookie";
document.cookie = "cookie3=" + escape(value) + "; expires=" + expires.toGMTString();

A Simple Cookie Application

The following simple personalized Web page illustrates the techniques for working with Cookies in JavaScript. If a user comes to the page for the first time or after the relevant Cookie has been deleted, they are prompted for their name. On future visits the user is welcomed personally without any prompting for their name:

<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
// SET UP EXPIRY DATE - ONE YEAR IN FUTURE
var expires = new Date();
expires.setTime (expires.getTime() + 24*60*60*365*1000);
// EXTRACT COOKIE
function getCookie(name) {
   var cookieFound = false;
   var start = 0;
   var end = 0;
   var cookieString = document.cookie;
   var i = 0;
   //LOOK FOR name IN cookieString
   while (i <= cookieString.length) {
      start = i;
      end = start + name.length;
      if (cookieString.substring(start,end) == name) {
         cookieFound = true;
         break;
      }
      i++;
   }
   //CHECK IF NAME WAS FOUND
   if (cookieFound) {
      start = end + 1;
      end = cookieString.indexOf(";",start);
      if (end < start)
         end = cookieString.length;
      return unescape(cookieString.substring(start,end));
   }
   //NAME WAS NOT FOUND
   return "NoName";
}
//CHECK IF USER'S NAME IS STORED
var userName = getCookie("UserName");
if (userName == "NoName") {
   userName = prompt("Please Enter Your Name:","Name");
}
document.cookie = "UserName=" + escape(userName) + "; expires=" + expires.toGMTString();
</SCRIPT>
</HEAD>
<BODY>
<H1>Personalized Home Page</H1>
<H2>Your Name is: <STRONG>
<SCRIPT LANGUAGE="JavaScript">
document.write(userName);
</SCRIPT>
</STRONG>
</H2>
</BODY>
</HTML>

This produces results like those shown in Figures 9.3 and 9.4.

Figure 9.3. The first time a user visit's the page they are prompted for their name.

Figure 9.4. On future visits the name is displayed automatically.

This script has three main parts: the getCookie() function, an initialization process, and the body of the HTML document.

The getCookie() function accepts a Cookie name as an argument. It then searches through the Cookie string returned by document.cookie for an occurrence of the name and if it finds it, returns the value associated with the name after it is passed through unescape(). If no Cookie exists, an empty string is returned.

The script in the header of the document also contains some initialization code that executes before the body of the document is parsed and rendered. This code calls getCookie() looking for a Cookie named UserName. If there is no Cookie with that name, then the user is prompted for his or her name. The value contained in userName (either from the Cookie or from the user) is written back to the Cookie UserName—ensuring that even if the Cookie existed, the expiry date is updated to be one year from the current date.

Finally, the user's name is displayed in the body of the document by outputting the value of the variable userName.

A Generic Cookie Manager


Just as framesets benefited from a generic library of code for working with them, so do Cookies. Rather than performing the work of setting expiry dates, encoding values, and parsing document.cookie for Cookies, a generic Cookie manager can provide a complete set of functions for working with Cookies.



Not only has Bill Dortch has written a full-scale frameset manager, he also has written and makes available a Cookie toolkit at http://www.hidaho.com/cookies/cookie.txt.

A Cookie toolkit requires several key functions:

Once assembled, a generic Cookie toolkit would look like:

<SCRIPT LANGUAGE="JavaScript">
//SET A NEW COOKIE
//Arguments:
//  name: cookie name (string)
//  value: cookie value (unencoded string)
//  expiry: expiry date (date)
//  path: document path (string)
//  domain: document domain (string)
//  secure: secure required? (boolean)
function setCookie(name,value,expiry,path,domain,secure) {
   var nameString = name + "=" + value;
   var expiryString = (expiry == null) ? "" : "; expires=" + expires.toGMTString();
   var pathString = (path == null) ? "" : "; path=" + path;
   var domainString = (path == null) ? "" : "; domain=" + domain;
   var secureSring = (secure) ? "; secure" : "";
   document.cookie = nameString + expiryString + pathString + domainString + secureString;
}
//GET A NEW COOKIE
//Arguments:
//  name: cookie name (string)
function getCookie(name) {
   var cookieFound = false;
   var start = 0;
   var end = 0;
   var cookieString = document.cookie;
   var i = 0;
   //LOOK FOR name IN cookieString
   while (i <= cookieString.length) {
      start = i;
      end = start + name.length;
      if (cookieString.substring(start,end) == name {
         cookieFound = true;
         break;
      }
      i++;
   }
   //CHECK IF NAME WAS FOUND
   if (cookieFound) {
      start = end + 1;
      end = cookieString.indexOf(";",start);
      if (end < start)
         end = cookieString.length;
      return unescape(cookieString.substring(start,end));
   }
   //NAME WAS NOT FOUND
   return "";
}
//DELETE A COOKIE
//Arguments:
//  name: cookie name (string);
function deleteCookie(name) {
   var expires = new Date();
   expires.setTime (expires.getTime() - 1);
   setCookie(name,"Delete Cookie",expires,null,null,false);
}
</SCRIPT>

Summary


Cookies provide a useful mechanism for maintaining client-side state information between pages in a session, and between sessions for the same user.

Using JavaScript, it is possible to create, read, and delete Cookies entirely in the client without any recourse to server-side programming and interaction. You can develop a generic Cookie toolkit to make the process of managing and working with Cookies easier.

In Chapter 10, "Applying Cookies and Frames," frames and Cookies are brought together to build an outline manager.

Previous Page Page Top TOC Next Page See Page