Free Web tutorials covering HTML, CSS, JavaScript, and DHTML from beginner to advanced. Free downloads and developer resources. Personalized help via email, form, and chat.

free, web, tutorials, HTML, html, CSS, css, stylesheet, cascading stylesheet, Javascript, javascript, JavaScript, DHTML, dhtml, beginner, advanced, web development, web page, web site, free web tutorial, free HTML tutorial, free CSS tutorial, free css tutorial, free cascading stylesheet tutorial, free stylesheet tutorial, free javascript tutorial, free DHTML tutorial, free HTML class, free CSS class, free stylesheet class, free cascading stylesheet class, free javascript class, free DHTML class">

Free Web tutorials covering HTML, CSS, JavaScript, and DHTML from beginner to advanced. Free downloads and developer resources. Personalized help via email, form, and chat. free, web, tutorials, HTML, html, CSS, css, stylesheet, cascading stylesheet, Javascript, javascript, JavaScript, DHTML, dhtml, beginner, advanced, web development, web page, web site, free web tutorial, free HTML tutorial, free CSS tutorial, free css tutorial, free cascading stylesheet tutorial, free stylesheet tutorial, free javascript tutorial, free DHTML tutorial, free HTML class, free CSS class, free stylesheet class, free cascading stylesheet class, free javascript class, free DHTML class

<Code_Punk>'s

Advanced JavaScript Lesson 36:
Shopping Cart "Remove" Feature

Code Tutorials



Site Development



Downloads



Help!!



Home


Overview

In this lesson, we'll be making this example. It looks much like the one in our previous tutorial, except for one important feature. It allows the viewer to remove items from the shopping cart.

When this remove feature is used, the cookie is updated. This updated cookie can then be further edited by adding items from the order pages or by making further removals.

In practice, the final cookie data is the final order and can be read by a backend program for processing. As you learn backend programming, you'll discover that cookies are a convenient way to pass JavaScript data to a Perl script or database.

In this example, I'm only using one cookie to hold the order data from both order pages. This simplifies things greatly as you will see. In practice, you'll try to limit your shopping carts to using one cookie if at all possible. It's almost always possible.

Setting The Cookie

The order pages set the cookie and the shopping cart page reads and displays the cookie. I used links for the viewer to click to order an item. These links look much like the links from the previous tutorial:

<a href="javascript:place('Ultra Widget')">Put In Shopping Cart</a>

Notice that this is the link to order the "Ultra Widget". The other items return different parameters.

The function "place()" is different from the "place()" function of the previous tutorial. It does three things.

1) The first thing the new "place()" function must do is to detect whether or not another a cookie has already been set. (Note in the example that the cookie name is "stuff=".)

function place(new_item){
if (document.cookie && document.cookie != ""){

Old Cookie Processing Will Go Here

}//ends IF

}//ends function

You should already be familiar with the above. If no cookie is found, we need to add an ELSE statement that will start one:

function place(new_item){
if (document.cookie && document.cookie != ""){

Old Cookie Processing Will Go Here

}//ends IF

else {
document.cookie = "stuff=" + escape(new_item + "xxx");
}//ends else


}//ends function

The above just writes a new cookie named "stuff=" and adds the parameter of the link clicked to the cookie. This is how a new cookie is started.

Once the cookie is set, the IF statement will detect it and we can store the old cookie, including the name and data, into a single variable and use that variable to add the old data before including any new items. Here's the code:

function place(new_item){
if (document.cookie && document.cookie != ""){

var old_cookie = document.cookie;
document.cookie = old_cookie + escape(new_item + "xxx");
}//ends IF

else {
document.cookie = "stuff=" + escape(new_item + "xxx");
}//ends else

}//ends function

There are two new statements in the above. The first sets the variable "old_cookie" to the value of the cookie - name and all (document.cookie). This is left escape()d. There is no need to unescape() it. It's only going right back on an updated cookie.

The second statement writes the updated cookie. It begins by adding the old data including the "stuff=" name and continues by adding the "new_item" parameter and our demarcation flag ("xxx"). It will overwrite any previous "stuff=" cookies.

Now we get an updated and demarcated cookie with each click of an order link. Both order pages use exactly the same function and append to the same cookie. Space could be saved by putting the "place()" function on an external JavaScript and linking it to all of the order pages. A big help if there are a lot of order pages.

Now, no matter what order or page an order link is clicked, we'll have a cookie containing a string that looks like this:

stuff=Item 1xxxItem 2xxxItem 3xxx ...

It will hold up to 4KB of data, that would be several hundred demarcated items as above. You can already see how easy this cookie will be to split() to make an array of the ordered items.

Basic Display

Now that we have our cookie set, it's time to read it and display the data. Getting the data from a single cookie is easier than the multiple cookie reading we did in the previous tutorial. First we make an array of ordered items. They're what's stored as data on the cookie:

var items = new Array();

if (document.cookie && document.cookie !=""){process_cookie()}

function process_cookie(){
var whole_cookie = unescape(document.cookie);
var sans_name = whole_cookie.split("=");
if (sans_name[1] != null){
items = sans_name[1].split("xxx");
}//ends IF
}//ends process cookie () function

You should recognize the above with the exception of the new IF in red. I began by making a new array() to put the cookie data in. I called this array "items". Next, I detected the presence of a cookie and sent any existing cookies to a "process_cookie()" function.

The "process_cookie()" function removes the "stuff=" name by splitting along the "=" sign. Next the demarcated data is split along the demarcation flags ("xxx") to provide the elements for the items() array.

Now lets talk about the part in red. We'll need to jump ahead a little bit. In some editing functions, it might be possible to have a cookie with a name but no data. The cookie string would look like this:

stuff=

Splitting this cookie in the "sans_name" statement would leave us with:

sans_name[0] = "stuff"

sans_name[1] = null or undefined

The IF statement in red prevents errors that would occur if we tried to process the "sans_name[1]" data when no data existed. If we tried to split "sans_name[1]" when "sans_name[1]" was null or undefined, we'd get an error. So, we test for null data first. If the data is, in fact, null, there is no reason to continue processing and we can just present a default "no order" display. (More on this later.)

Now we have any cookie data parsed and put neatly into the items() array ready for easy display. Here's the basic display code used. It's much like that from the previous tutorial except only one array is needed:

<body>
<form name="orderform">

<script language="JavaScript">

for (i = 0; i < items.length - 1; i++){

document.write("<input type=\"checkbox\" name=\"stuff\" checked ");

document.write("value=\"" + items[i] + "\">");

document.write(" " + items[i] + "<br>");

}//ends main FOR

</script>
</form> ...

First let me explain the "-1" in red above. This prevents the last item of the items() array to display. Why did I do this? Well, it goes back to our demarcation scheme. I put "xxx" at the end of each item. This means that even the last item is followed by "xxx".

When the data is split() around "xxx" that last set of "xxx"s is included. To the right of these "xxx"s is nothing or null. This adds a null item to the end of the items() array. In other words, items[last_one] = null or undefined. This causes an extra blank checkbox to display if it's allowed to be included in the display's FOR loop.

The "items.length - 1" prevents the loop from including this last, null or undefined, item. A similar thing also occurs in some circumstances when you add your demarcation in front of data: "xxxItem 1xxxItem 2". In this case, you may have a null item at the start of the split() array. In short, array_name[0] = null or undefined. To remove this array element from your display just start your FOR loop's counting at 1 instead of 0:

for (i = 1; i < array_name.length; i++) ...

The biggest difference between the above and our previous tutorial is the addition of the <input type="checkbox"> in the first "document.write()s". Care must be taken when writing HTML tags with JavaScript. Remember to use the escape character (\) to precede all quotation marks. Expect to do some debugging.

The "document.write()s" will produce an HTML <input> tag that looks like this:

<input type="checkbox" name="stuff" checked value="[item from cookie]">[item from cookie]<br>

One of these <input> tags with with descriptive text beside it would be written for each item processed from the cookie into the items() array.

The second difference is that the entire script is placed inside <form> tags. This is because we're making our display a form so that the checkboxes can be used to remove items from the cookie/order.

Once the display is presented, we can refer to the checkboxes just like with any other form:

document.orderform.stuff[which_box].value

Now we need to add some error control to our display in case there is no cookie or all items are deleted or we get null data. The first step is to determine how we can tell if there is no data. I used the items() array to help me. If there are no items ordered, then "items.length" will be zero:

<body>
<form name="orderform">

<script language="JavaScript">

if (items.length == 0){ document.write("<h4>You haven't ordered anything</h4>"); }//ends IF

else{

for (i = 0; i < items.length - 1; i++){

document.write("<input type=\"checkbox\" name=\"stuff\" checked ");

document.write("value=\"" + items[i] + "\">");

document.write(" " + items[i] + "<br>");

}//ends main FOR

}//ends ELSE

</script>
</form> ...

The above checks to make sure there's some data for the display to write. If not, a default display is presented by the IF's "document.write()" and the form is not displayed.

Since we do not want our form displaying unless there is data to show, we put the core display code in an ELSE statement. This covers all of the bases - data and no data to display. No other error control is needed. As an alternative we could have put our core display code in an IF statement reading:

if (items.length > 0) ...

Either way, we've parsed our cookie data into a form that can now allow the viewer the option of removing items. This is the first step to an editable shopping cart.

Kill Cookie Feature

Before jumping to the shopping cart's "remove" feature, lets look at the cookie killing feature. I made a cookie killing link on the page for your convenience when playing with the shopping cart. You wouldn't add this to a real application.

This doesn't mean that cookie killing isn't important. We'll need a cookie killing function when our "remove" feature removes all of the data. You may also want to add a "Cancel Order" button or link which should kill the cookie. On most occassions where a cookie is set or read, there will probably be a reason to kill the cookie and a kill cookie function is almost always included in every cookie script.

The cookie killing script used on the read page of our example is the same that we've used many times before:

function kill_cookies(){
var kill_date = new Date("January 1, 1970");

document.cookie = "stuff=stub;expires=" + kill_date.toGMTString();

}//ends kill cookies()

There shouldn't be anything new to you in the above.

Remove Feature

Now to the part of this tutorial that's really new. The remove feature. We've already done a lot of the work by having our display write out a checkbox form with appropriate VALUE attributes in the <input> tags. Now it's time to code the function.

The function is activated by the "Remove" button coded after the "document.write" loop, but before the </form> is closed. This button is not included in the previous coding examples for the sake of clarity and simplicity. Here it is:

...
document.write(" " + items[i] + "<br>");

}//ends main FOR

}//ends ELSE

</script>

<input type="button" value="Remove" onClick="remove()">

</form> ...

The button leads to a "remove()" function. The remove() function must do three things. 1) It must find out which items are still checked. 2) It must write this new list to the "stuff=" cookie thereby overwriting the old cookie. 3) It must reload the page to display the new cookie's data.

One key to this function lies in worrying about what was removed, but in what remains. This is what we want to do to get data for the new "stuff=" cookie.

We'll start the function by using a FOR loop to cycle through the checkboxes (document.orderform.stuff) and find out which ones are checked.

function remove(){

var new_cookie_stuff = "drop this";

for (i = 0; i < document.orderform.stuff.length; i++){

if (document.orderform.stuff[i].checked == true){
new_cookie_stuff = new_cookie_stuff + document.orderform.stuff[i].value + "xxx";
}//ends if

}//endsFOR

... }//ends remove() function

There's a lot to talk about here before moving on. First, is the part in red "checked == true". A common syntax error made when people cycle through and array of checkboxes looking for those checked is that they use:

document.form_name.boxes_name[which_one] == checked

OR

document.form_name.boxes_name[which_one].checked

The proper syntax is shown in the code. It's "checked == true" for checked boxes, or "checked == false" for unchecked boxes. Remember that "checked" is a property of checkboxes, not a value. The "checked" property can have a value of true (checked) or false (unchecked).

Now lets talk about the first line:

var new_cookie_stuff = "drop this";

This is the initialization for the string we're making with the FOR loop. When building a string with a FOR loop, the string's variable should be initialized outside of the loop. This is so it won't be re-initialized to the current value each time the loop runs. If it's re-initialized, it won't keep adding to itself. It will only store the last value.

I chose a value of "drop this" to aid in the "cleaning" process later. I'll be using a split() to remove it from the final string for the new cookie. If "null" or a blank string ("") were used to initialize the variable, it would be added as the first part of the string and would be more difficult to detect and remove using split().

After this loop is run, say with two items that are still checked, the string stored in "new_cookie_stuff" would look like this:

drop thisItem 1xxxItem 2xxx

Now all we need to do is clean up this string by removing the "drop this" with split() and putting the rest in a variable to write to our new cookie.

function remove(){

var new_cookie_stuff = "drop this";

for (i = 0; i < document.orderform.stuff.length; i++){

if (document.orderform.stuff[i].checked == true){
new_cookie_stuff = new_cookie_stuff + document.orderform.stuff[i].value + "xxx";
}//ends if

}//endsFOR

var clean_cookie_string_1 = new_cookie_stuff.split("drop this");

var clean_cookie_string = clean_cookie_string_1[1];


}//ends remove() function

The first new line splits the "new_cookie_stuff" string we built with our FOR statement around the "drop this" string. This leaves us with a "clean_cookie_string_1" array of:

clean_cookie_string_1[0] = null or undefined

clean_cookie_string_1[1] = Item 1xxxItem 2xxx ...

Now all we have to do is store the clean_cookie_string_1[1] data into a variable to write to our cookie. This is done by the second line of code: "var clean_cookie_string = clean_cookie_string_1[1];"

Now our remaining checked items have been discovered and written to a demarcated string ready for writing to our new "stuff=" cookie. The only trick to writing the cookie is to make sure there actually is some data in "clean_cookie_string" or if the cookie needs to be killed:

function remove(){

var new_cookie_stuff = "drop this";

for (i = 0; i < document.orderform.stuff.length; i++){

if (document.orderform.stuff[i].checked == true){
new_cookie_stuff = new_cookie_stuff + document.orderform.stuff[i].value + "xxx";
}//ends if

}//endsFOR

var clean_cookie_string_1 = new_cookie_stuff.split("drop this");

var clean_cookie_string = clean_cookie_string_1[1];

if (clean_cookie_string != ""){

document.cookie = "stuff=" + escape(clean_cookie_string);

}//ends IF


window.location = "samp174.htm";

}//ends remove() function

The "if (clean_cookie_string != "")" insures that there is data to write to the new cookie. (If there's not, we'll be killing the cookie in the next step.) If there is a "clean_cookie_string" with data, then we write it to the "stuff=" cookie and, thereby, overwrite the current "stuff=" cookie.

After the cookie is written, I reload the shopping cart page with the "window.location" statement.

If no data in the "clean_cookie_string", I detect it and kill the whole cookie:

function remove(){

var new_cookie_stuff = "drop this";

for (i = 0; i < document.orderform.stuff.length; i++){

if (document.orderform.stuff[i].checked == true){
new_cookie_stuff = new_cookie_stuff + document.orderform.stuff[i].value + "xxx";
}//ends if

}//endsFOR

var clean_cookie_string_1 = new_cookie_stuff.split("drop this");

var clean_cookie_string = clean_cookie_string_1[1];

if (clean_cookie_string != ""){

document.cookie = "stuff=" + escape(clean_cookie_string);
window.location = "samp174.htm";

}//ends IF

if(clean_cookie_string == ""){kill_cookies()}

window.location = "samp174.htm";

}//ends remove() function

The new code in red detects a null ("") string. If "clean_cookie_string" is null, the "kill_cookies()" function is run killing the whole cookie - name and all. The script then automatically returns to the line that called the kill_cookie() function and runs the next line. The next line is the "window.location" that reloads the page to show the "no order" display.

One Last Possible Error

An error will occur with the script above if the viewer clicks the "Remove" button when they've not ordered anything - there's no cookie or cookie data. What happens is that the remove() function looks for the checkbox array "document.orderform.stuff" in the FOR statement. If there's no cookie data, these checkboxes aren't written and don't exist. A "no object" error will occur.

To prevent this we need to go back and add an IF to the remove() function that will detect this error. It should check for the existence of "document.orderform.stuff":

function remove(){

var new_cookie_stuff = "drop this";

if (document.orderform.stuff){

for (i = 0; i < document.orderform.stuff.length; i++){

if (document.orderform.stuff[i].checked == true){
new_cookie_stuff = new_cookie_stuff + document.orderform.stuff[i].value + "xxx";
}//ends if

}//endsFOR

}//ends no "stuff" array IF

var clean_cookie_string_1 = new_cookie_stuff.split("drop this");

var clean_cookie_string = clean_cookie_string_1[1];

if (clean_cookie_string != ""){

document.cookie = "stuff=" + escape(clean_cookie_string);
window.location = "samp174.htm";

}//ends IF

if(clean_cookie_string == ""){kill_cookies()}

window.location = "samp174.htm";

}//ends remove() function

The highlighted IF statement above will prevent the loop from running if there are no "document.orderform.stuff" boxes. Notice that I check for the existence of "document.orderform.stuff" boxes, and not for a null or other value. I'm checking to see if the array object exists. Again, this must be done before the FOR loop is run and asks for the length of the "document.orderform.stuff" array.

Also note that the variable "new_cookie_stuff" is declared before the new IF even though the loop may not run. This is to prevent errors in later statements (outside the loop) to set or kill the cookie requiring a value for "new_cookie_stuff.

Summary & Exercises

When making a regular shopping cart where people can remove items, it's best to use a single cookie that has data added to it. This single cookie is easier to parse and edit than multiple cookies using multiple data.

The Remove Feature is made in five steps:

1) A variable is initialized to hold the new string that will contain the data that is still checked. Worry about what is left, not what was removed. This variable will be initialized to a unique string that can be easily dropped later with split().

2) A FOR loop is run to see which items are still checked. Checked items are added to the string initialized in step 1 above. An IF statement must test for the presence of the checkbox array before this FOR loop is run. Don't forget to add your demarcation flags ("xxx")!

3) The string made by the FOR statement must be cleaned up. The initial string must be dropped and the remaining data stored in a separate variable.

4) The final data string must be tested for the presence of data. If data is present, a new cookie is written with "document.cookie". If the string is null, the whole cookie is killed.

5) The page must be reloaded so the new cookie (or absence of one) can be read and the new data displayed.

For practice, code a simple two or three page shopping cart from scratch. Now try updating the pages by adding new items and new pages.



To Next Advanced JavaScript Lesson

Back To Advanced JavaScript Index