The underlying way that browsers and servers interact (HTTP: the
hypertext transport protocol) is stateless. What that means is
that the server doesn't remember
that it has interacted with your
browser before. The second time, tenth, millionth time is just the same
as the first time.
This is convenient for implementing web servers, but it's inconvenient for users and web applications. Having state allows much better experiences for users.
What do we mean by state? Anything that depends on the history of your interactions with the website:
One of the oldest and still most important solutions to providing stateful interactions on the web is cookies. A cookie is a like a ticket from a dry cleaners: just as you hand the ticket to the clerk and expect to get back your own garments, the browser hands the cookie to the server along with the request for the web page, and it can get back a customized page.
Today, we'll learn about cookies and how to use them to do interesting things.
In the HTTP protocol (the P
in HTTP), the browser and the
server talk to each other to get/send web pages. A typical request
(from browser to server) looks like this:
Typical reply (from server to browser):
HTTP/1.0 200 OK Date: Fri, 20 Apr 2010 16:20:00 GMT Server: Apache/1.3.9 (Linux) Content-Length: 141 Content-Type: text/html <html> <body> <p>The web page stuff. </body> </html>
cookie jarand sends all the unexpired cookies that match the domain and path.
What's in your cookie jar? Take a couple of minutes to poke around in your browser to find the cookies. These things move from version to version, but as of this writing, you can find cookies:
remove individual cookies
show cookies.)
Since cookies live on the client, what does that mean in terms of privacy and security? Here are a few common scenarios:
/Applications/Firefox), but my personal stuff and your
personal stuff are in folders that belong just to us
(/Users/sanderso/ versus /Users/ehildret/
versus /Users/youracct/). So, this is more vulnerable than
a machine you have physical control of, but it is still relatively
secure. Someone would have to hack into your account to view your
cookies.
The document returned by the server might have some JavaScript in it, and the JavaScript code can also look at the cookie and customize things. The JavaScript code can also set cookies. This allows you to have some of the benefits of cookies with static HTML pages or a server CGI script that doesn't use cookies. The picture looks almost the same:
Here are some things we might remember in a cookie:
idto identify repeat visitors
Let's look at some of these possibilities. All of them will be based
on two JS functions: setCookie and getCookie.
These functions are not built-in to the JavaScript
language. Earlier, we learned how to load JavaScript code from an
external file; these functions are defined in a file in the CS110
account: http://cs.wellesley.edu/~cs110/js/cookies.js.
You would load that library file in the head of your web
page, so that the functions will be defined before they are used.
Using the power of abstraction, we won't study the code of those functions, though it's within your skills. (See below if you're curious.) All you really need to know about the functions is the following:
Before we start using these functions, though, let's take a brief detour that will help us test our use of these functions.
We could always look up the document's cookies using one of the techniques that we saw in the cookie inspection exercise, but that can get tedious. Since JavaScript can look at the document's cookies, I've implemented the little box fixed at the upper right that will let you set, show or clear the document's cookies whenever you like.
Try setting a cookie using the form and then displaying it. You can also see it in more detail by inspecting the browser's cookie jar.
To set a cookie from JavaScript code, you can use
the setCookie() function defined in
the cookies.js
file. This function has two required arguments: the name and value of
the cookie (both strings).
The next argument, expire_days, is optional. If you omit
it, a cookie is set that expires when the browser closes. If you supply
that argument, it's the number of days that the cookie should last (an
integer).
The path argument is also optional. You can use it to
restrict the pages within the site that share the cookie. You might set
it to "/" if you want every page in your domain (e.g. cs.wellesley.edu)
to match; that would be the least-restrictive option. You'll see that
in most of the cookies in your browser. On Puma, you could set it
to "/~youraccount/" to restrict the cookie to just your
account. That means that another student whose web pages are also on
Puma, would not be able to read or modify the cookies associated with
your pages. For that reason, you should probably restrict it as much as
reasonable, just to avoid inadvertently interfering with cookies for
other pages.
Example: The following code sets the cookie named "zip_code" to a value gotten from the user, default value of 02481. It will expire in a week. Try it, and look at the values in the cookie jar!
The second function, which is also defined in cookies.js, is much easier. It takes just one argument, the name of the cookie (a string) and returns its value, also a string. If there is no such cookie, the function returns the empty string.
Example: The following code gets the cookie named "zip_code," if any.
The following text is dynamically written based on the value of a cookie:
Suppose we want to do something special for a first-time visitor, but not for return visits. Maybe a special introduction or greeting, as we did in the green box at the top of this section. How can we tell whether someone has visited this page before? We could set a cookie!
We might set a cookie named "first_time" with a value of "no." Also, we have to consider that for a first-time visitor, no cookie will be set, so we have to notice the absence of the cookie. Your cookie code should always be aware of the possibility that there may be no cookie at all.
The code is here:
/* Code to remember whether someone has visited before.
Relies on cookies.js
Scott D. Anderson
Fall 2009
*/
// 1. Try to get the cookie
var first_time = getCookie("first_time");
// 2. respond appropriately
if ( first_time == "no" ) {
document.writeln('Welcome back!');
} else {
document.writeln('We hope you enjoy your first visit!');
}
// 3. Set it for the next visit. (Expires in a month)
setCookie("first_time","no",30);
Let's talk through the code briefly:
first_time variable will have a value equal to the
empty string.
Note that we put that code (inside script tags or loaded
from an external file using the script tag) exactly where
in the body of the page we want the text to appear. We don't put that
code into the head.
Here's that idea in a separate web page
distinguishing first visits from returns. View the source and
look for the two script elements; everything else is
irrelevant.
Websites like Amazon.com, Facebook, and many others work like this, only instead of just recording a value like "no," they also (or instead) save some unique identifier, which is usually a very long string of gibberish, and they use the unique identifier to look up your name and account info in a database.
A slight extension of this idea is to count the number of times this person has visited. (Maybe they get a prize after a certain number of visits.) All we have to do is create a cookie that stores the number of visits. Of course, we have to keep in mind that the cookie might not be set at all.
The code is here:
/* Code to count visits
Relies on cookies.js
Scott D. Anderson
Fall 2011
*/
// 1. get the cookie
var visits = getCookie("visit_count");
// 2. figure out number of prior visits
var visit_count;
if ( visits == "" ) {
visit_count = 0;
} else {
visit_count = parseInt(visits);
}
// 3. respond appropriately
document.writeln("You have visited this site "+visit_count+" times before.");
if( visit_count == 0 ) {
document.writeln("Thanks for coming!");
} else if (visit_count > 10) {
document.writeln("Wow, I'm glad you like our site!");
}
// 4. Increment the counter, expire in a year
setCookie("visit_count",visit_count+1,365);
Here's that idea in a separate web page with
visit counter. View the source and look for the
two script elements; everything else is irrelevant.
Notice two important facts:
visit_count cookie is shared by several pages, so
the separate page above shows a count that includes visits to these
notes. There are many ways to vary this code. If you wanted to, you
could make only your home page (for example) increment the counter, so
although all the pages could see the counter, only visiting the home
page would increment it. An even fancier trick would be to increment
the counter only on the first visit of a day, so the counter would
count the number of different days this person visited. I'm sure you
can think of other variations.
If we can remember the number of times a user has visited, perhaps we
can remember some customization that the user chooses. In this example,
we will allow a user to choose her background image. We'll do this in
two steps. First, we'll look at how to choose the background image
without saving the value in a cookie. Then, we'll add the cookies so
that the choice sticks
.
Here's the first version:
All of the HTML will be familiar to you. The CSS might be a little
new. There is
a background-image
CSS property that allows you to set a background image for an element.
The value is the URL of the image, wrapped in the
special url() syntax:
body { background-image: url('images/flowers.jpg'); }
In JavaScript and the DOM, we can modify that CSS property by assigning
to the elt.style.backgroundImage.
Another slight bit of trickiness is that we use a new event handler
called onChange. As you might guess,
the onChange
event handler gets executed when the form value changes — here,
that's when we change our menu selection.
The second version is almost identical. There are three differences:
cookies.js file
onChange event handler
Let's see it in action before we look at the code:
Saving the cookie is a fairly simple extension of the code in
our onChange event handler. We make the value of the
cookie be exactly the value that we want to assign to
the backgroundImage property.
Reading the cookie is the tricky part — in
particular, when to read it. A good choice is to have it run
in the onLoad event handler.
The onLoad
event handler is run when the page finishes loading. It's specified as
an attribute of the body tag, and it looks like this:
<body onload="alert('page has finished loading');">
In our case, of course, we do the following:
<body onLoad="document.body.style.backgroundImage=getCookie('bgimage_choice');">
This just takes the value of the cookie and assigns it to the style property. Works great!
But what if there is no cookie? In that case, we'll end up assigning the empty string, but that has the effect of unsetting the background image, which works fine.
In an earlier lecture, we learned about how we could create an accessibility bar. That code didn't have any ability to save the font-settings, though, so the user would have to re-choose the settings every time she comes to the page. That's annoying at best.
We will create a simple box with two font-size changers in it. They will allow the user to increase or decrease the font-size. Let's see it in action first
There are a few tricky parts of the code. First, let's look at the underlying JavaScript, which is very short:
/* simple code to increase/decrease font size
from an initial value of 12px.
Scott D. Anderson
scott.anderson@acm.org
November 2011
*/
var FontSize=12; // in px
function changeFontSize(diff) {
FontSize = FontSize + diff;
document.body.style.fontSize=FontSize+"px";
}
A single global variable keeps track of the current font size. Just one
function is defined: it adds its argument to the global variable, and then
sets the fontSize style property on the body to
the desired font size. This one function takes care of both increasing
and decreasing the font size: if called with a positive number, the font
gets bigger, and if it's called with a negative number, the font gets
smaller. If called with zero, it doesn't change the global variable, but
it does set the document's font size to the value of the global variable.
The HTML code looks like this:
<body onload="changeFontSize(0);"> <div id="font_widgets"> <a href="javascript:changeFontSize(-1);">a</a> <a href="javascript:changeFontSize(+1);">A</a> </div>
Note the odd-looking href parts of the hyperlinks.
Because they start with javascript:, these execute the code
in the href, instead of taking us to another URL. This is a convenient
way to allow your user to interact using links instead of form buttons.
Improve the example above so that it uses cookies to remember the user's chosen font size. You'll have to:
fontsize.js file.
FontSize variable should initialized to 12.
FontSize global variable to the value stored in the cookie.
fontsize.js file so that whenever the
font size is changed, the cookie's value is also changed.
The National Weather Service is good enough to provide a nice forecast site (your tax dollars at work), but it doesn't use cookies to remember your location. (It would have been so easy.)
Here's a page that lets the user show the weather by filling out a form.
The form above allows the user to give her zip code and get the forecast included in her page. Using cookies, have the browser remember her zip code and automatically load the weather forecast.
To do:
cookies.js library file.
save zip.
use saved zipbutton.
You could imagine getting the cookie in an onLoad
event handler, so that if there is a cookie, the page will
automatically load the weather forecast for that place, without the
user having to ask.
There are many legitimate uses for cookies, and most modern websites can't adequately function without them, so they're really indispensable for features such as:
Nevertheless, there can be a dark side to cookies:
clickstream) (possibly with embedded query information), software configuration (OS and browser), and the contents of any JavaScript variable.
DoubleClick was a company founded in 1995 that managed to use cookies to track individuals' behavior across websites. Websites that used DoubleClick's ads included absolute links to DoubleClick's ad server, which could then set a cookie as it responded to the image request. The cookie for ad.doubleclick.net could then track which websites the user visited, and this allowed the ads to change based on users' browser behavior. Google eventually acquired DoubleClick in 2008.
From the W3C Security FAQ, some more information about cookies and their security risks.
A cookie is just a small amount of data, saved on your local computer, that the browser will send back to the server whenever you visit that site. That allows the server to recognize you and, possibly, provide a customized response. The cookies can also be read and written by JavaScript code.
A condensed version of these notes can be found here. See a demonstration of a modified version of Wendy Wellesley's home page that counts the number of times the user has visited the page.
Beyond here is information that we think you might find interesting and useful, but which you will not be responsible for. It's for the intellectually curious student.
Some of the examples above asked the user for some information (such as
their name) using prompt(). Of course, we can also ask them
for information using forms. This means that we can save the data in a
form using cookies.
Why would we want to do this? Often websites don't want to process incomplete or partial forms, and saving partial forms could be a hassle. However, they could easily allow the data to be saved using cookies. Many drafts could be saved this way, with zero burden on the server. When the user is finally happy with the form, they submit it as usual.
Here's an example of a saveable form. The code is a little outside what we've covered in this course, but just a little. If you'd like to do something like that for your site, talk to your advisor.
There are a few tricks to the cookie functions that we have been using, but only a few, so the essentials of the code are reasonably understandable. If you're curious, read the code for ../../cookies.js.