reptile7's JavaScript blog
Wednesday, May 27, 2009
 
At the Top of the Slide
Blog Entry #146

Back in Blog Entry #44, we discussed HTML Goodies' JavaScript Primers #27, which presents a script that codes a cyclic display of a series of images through which the user moves via clicking a link - a simple slide show in effect. Primer #27's Assignment, which we did not discuss in Blog Entry #44, addresses the corresponding code for running the Primer #27 slide show in reverse. Joe reprised the Primer #27/Assignment scripts for a subsequent "So, You Want A Slide Show, Huh?" tutorial, our focus today.

For the "So, You Want A Slide Show, Huh?" tutorial, Joe combines his original slide show code with a new set of four images - information.gif, interference.gif, message.gif, and nervous.gif - and also places under the slide show img placeholder a text field for holding information about the displayed image. The tutorial's new-and-improved slide show code is reproduced in the div below:

<script type="text/javascript">

var num = 1;
img1 = new Image( );
img1.src = "information.gif";
img2 = new Image( );
img2.src = "interference.gif";
img3 = new Image( );
img3.src = "message.gif";
img4 = new Image( );
img4.src = "nervous.gif"; 

text1 = "Text for Picture One";
text2 = "Text for Picture Two";
text3 = "Text for Picture Three";
text4 = "Text for Picture Four";

function slideshowUp( )
{
num = num + 1;
if (num == 5)
{ num = 1; }
document.mypic.src = eval("img" + num + ".src");
document.joe.burns.value = eval("text" + num);
}

function slideshowBack( )
{
num = num - 1;
if (num == 0)
{ num = 4; }
document.mypic.src = eval("img" + num + ".src");
document.joe.burns.value = eval("text" + num);
}

</script>

<!-- The Image and Form Codes are Below --> 

<center>
<img src="information.gif" name="mypic" border="0" height="100" width="100" alt="" />
<p>

<form name="joe" action="">
<input type="text" width="100" name="burns" value="Text For Picture One" />
</form>

<a href="JavaScript:slideshowBack( );"> Back</a>

<a href="JavaScript:slideshowUp( );"> Next</a>

Blog Entry #44's analysis of the Primer #27 script also applies to the "So, You Want A Slide Show, Huh?" script, so there's no need to do a detailed deconstruction of the latter in this post. But in his general remarks at the beginning of Primer #27, Joe exhorts us to try to figure out how you can make the script a little different, a little better - that's the least we can do for the "So, You Want A Slide Show, Huh?" code, wouldn't you say?

Automating the preloading of images

The tutorial script's script element first declares a num variable that will serve as an index number for both the slide show images and the captions under those images. Next, the script preloads the slide show images so that the user isn't kept waiting for those images to download when moving through the slide show:

var num = 1;
img1 = new Image( );
img1.src = "information.gif";
img2 = new Image( );
img2.src = "interference.gif"; // etc.
/* In the tutorial's "How It Works" section, Joe completely glosses over the preloading code; [t]he four images are listed follow[ing] a traditional format is all he says about it. */


The slide show images have ordinal object references: img1, img2, img3, and img4; if they also had ordinal file names - say, pic1.gif, pic2.gif, pic3.gif, and pic4.gif, à la the Primer #27 slide show - then we could use a for loop to automate the preloading process, as follows:

var img = new Array( );
for (i = 1; i < 5; i++) {
img[i] = new Image( );
img[i].src = "pic" + i + ".gif"; }


It's not a big deal to respectively write out the preloading code for each image for a slide show with four slides, but you wouldn't want to do that (or at least I wouldn't want to do that) for a slide show with fifty slides, in which case a loop is the way to go. The downside to automated preloading is that the images cannot have descriptive (non-ordinal) file names, but IMO that's a small price to pay to keep the volume of preloading code from getting out of hand.

Ditching eval( )

The tutorial script uses separate slideshowUp( ) and slideshowBack( ) functions to move through the slide show forwards and backwards, respectively. Both of these functions use the following commands to load a new image and caption into their respective placeholders:

document.mypic.src = eval("img" + num + ".src");
document.joe.burns.value = eval("text" + num);


The top-level eval( ) function is one means via which we can leverage num as an index number and thus use the same assignment statements for all four slides. Joe's explanation of eval( )'s role here - The eval( ) helps to turn the text in the parentheses into a variable name rather than just text - could definitely use some help. Admittedly, eval( ) is a somewhat abstract function: eval( ) acts on a stringified expression and returns the value of that expression in its unstringified form. For example, if num is 1, then with respect to the original script, eval("img" + num + ".src") returns information.gif, the value of img1.src. So returning to Joe's reading of eval( ), we might alternatively and more accurately say that eval( ) turns the expression in the parentheses into a relevant value for assignment to document.mypic.src or document.joe.burns.value.

Interestingly, Mozilla criticizes the use of eval( ), stating that eval( ) constitutes a security risk and is slow and should be avoided whenever possible. Although I'm not sure that these considerations apply to the "So, You Want A Slide Show, Huh?" script, it is nonetheless worth asking: How might we move through the slide show in an eval( )-less manner? Gratifyingly, our good friend Mr. Array can provide a "safe alternative" in this case.

You may have noticed that the for loop in the preceding section not only preloaded the slide show images but also arrayed the images' object references. After the preloading code, the script itemizes and variabilizes the four slide show image captions; let's say we array them too:

var text = new Array( );
text[1] = "Text for Picture One";
text[2] = "Text for Picture Two";
text[3] = "Text for Picture Three";
text[4] = "Text for Picture Four";


We can now change slides much more straightforwardly via the following assignments:

document.mypic.src = img[num].src;
document.joe.burns.value = text[num];


Ciao, eval( ) - maybe we'll see you in the afterlife. ;-)

N.B. As img and text are both HTML name tokens, perhaps they are not the best choices for variable names - I chose these identifiers to be consistent with Joe's code - but neither of them is a JavaScript reserved word, so they're not invalid, either.

Triggering slideshowUp( )/slideshowBack( )/slideshow( )

The user goes through the slide show forwards and backwards by respectively clicking Next and Back hyperlinks, whose underlying anchor elements are equipped with JavaScript URLs for triggering the slideshowUp( ) and slideshowBack( ) functions:

<a href="JavaScript:slideshowBack( );"> Back</a>
<a href="JavaScript:slideshowUp( );"> Next</a>


My response: "Yuck." I have previously expressed my disdain for the use of anchor elements as user interface elements, which, despite it being a very widespread practice, I view as a clear-cut violation of the W3C's "Use markup and style sheets and do so properly" accessibility guideline (see also the "A Shift Towards Semantic Mark-up" section at the beginning of HTML Goodies' "CSS Layouts Without Tables" article). A hyperlink should be used to link to another resource (either another page or another section of the same page); for calling a JavaScript function via a click event, a push button is a much better choice:

<button type="button" onclick="slideshow('previous');">Back</button>
<button type="button" onclick="slideshow('next');">Next</button>


Note that my buttons are not associated with the slideshowUp( )/slideshowBack( ) functions but instead with a common slideshow( ) function, bringing us to...

Merging slideshowUp( )/slideshowBack( )

Because the slideshowUp( ) and slideshowBack( ) functions change slides with the same commands (vide supra), why not merge them? This is easily done with a bit of conditional code:

function slideshow(direction) {

if (direction == "next") { num++; if (num == 5) num = 1; }
if (direction == "previous") { num--; if (num == 0) num = 4; }

document.mypic.src = img[num].src;
document.joe.burns.value = text[num]; }


Per the push buttons above, slideshow( ) is passed either next or previous, and then one or the other if statement increments, decrements, or resets the num index, as appropriate.

One more thing...

The script's display is centered by a deprecated center element and the mypic img placeholder is equipped with a deprecated border="0" attribute - I trust you are up to the task of converting these guys into corresponding style rules. But something else in the script's HTML caught my eye: the burns text box has been given a width="100" attribute

<input type="text" width="100" name="burns" value="Text For Picture One" />

which has never been W3C-valid for the input element. The MSDN Library lists a width attribute for the <input type="text"> element, but on my computer MSIE 5.2.3 for Mac OS X does not support it.

Width is a presentational feature, which raises the question: Can we set the CSS width property for a text box? At first glance it wouldn't seem so, because the CSS width property applies to all elements but non-replaced inline elements, table rows, and row groups, and the input element is to my understanding a "non-replaced inline element". However, Section 3.2 of the CSS 2.1 Specification states:
CSS 2.1 does not define which properties apply to form controls and frames, or how CSS can be used to style them. User agents may apply CSS properties to these elements. Authors are recommended to treat such support as experimental. A future level of CSS may specify this further.
In the event, all of my OS X browsers - Firefox, MSIE, Opera, and Safari - smoothly write the width property for the burns field; in practice, I find that an

input { width: 120px; }

rule gives a satisfactorily sized box.

But maybe you would rather just trade in the width="100" attribute for a size="25" attribute - that would also work.

In the next post, we'll roll out a demo based on the above code, plus we'll respond to some of the comments at the bottom of the tutorial.

reptile7

Monday, May 18, 2009
 
You're Gonna Lose That Cookie
Blog Entry #145

We continue today with our analysis of HTML Goodies' "So, You Want To Set A Cookie, Huh?" tutorial. In the "Fetch" section of the previous entry, we mounted on a slide and placed under the microscope the tutorial's demo page, which with Internet Explorer (specifically, MSIE 5.2.3 for Mac OS X) works as advertised, more or less, correctly returning the user's cookie value for display on the page's first line. But what about other browsers, huh? After setting a dataCookie=oatmeal raisin cookie with the putCookie( ) function, the demo page display when using Firefox, Opera, or Safari (again excluding the <<...Back link) is

You Entered No cookie name found.dataCookie.
No cookie matching was found
You Entered:

if the text box value remains unquoted - not what we want, needless to say.

When I visit the "So, You Want To Set A Cookie, Huh?" page with the above non-MSIE browsers, HTML Goodies writes five cookies to my hard disk; the demo page detects these cookies: the if (document.cookie) condition in the getName( ) function returns true. The demo page does not detect the dataCookie=oatmeal raisin cookie, however. Here are the code-level details:

• In the getName( ) function, document.cookie.indexOf(cookie_name) returns -1; with index = -1, getName( ) returns and assigns to YouWrote No cookie name found. + dataCookie (Test), which is written to the demo page's first line.

To refresh your memory, the getName( ) function as it appears in the demo page source is reproduced in the div below:

function getName() {
    if(document.cookie) {
        var Test=cookie_name;
        index = document.cookie.indexOf(cookie_name);
        if (index != -1) {
            namestart = (document.cookie.indexOf("=", index) + 1);
            nameend = document.cookie.indexOf(";", index);
            if (nameend == -1) {
                        nameend = document.cookie.length;
            }
            YouWrote = document.cookie.substring(namestart, nameend);
            return YouWrote;
        } else {
                return "No cookie name found." + Test;
        }
    } else {
        return "No cookie ID found.";
   }
    
}

YouWrote=getName();

• In the testCookie( ) function, name (dataCookie) is not found in cookieString (document.cookie) in any of the while loop's iterations, and thus the Boolean cookieFound variable stays at its initial value, false; consequently, testCookie( ) returns and assigns to testcx No cookie matching was found, which is written to the demo page's second line.

So why does MSIE detect the dataCookie=oatmeal raisin cookie but the other browsers don't? Let's go back and take a closer look at the putCookie( ) statement that writes the cookie:

document.cookie = cookie_name + "=" + YouEntered
+ "; expires=Monday, 04-Apr-2010 05:00:00 GMT";


The above command sets a name value, a value value, and an expires value for the cookie, but it doesn't set a domain value or a path value; in contrast, the great majority of cookie-setting Web sites set both domain and path values for their cookies. Here is the $64,000 question: Although the domain and path attributes are in fact optional when writing a cookie, what happens if we don't specify values for these attributes?

The "Parameters" section of the "Netscape Cookies" Appendix of the JavaScript 1.3 Client-Side Reference directly addresses our situation:
If you do not specify a value for [the domain attribute], Navigator uses the host name of the server which generated the cookie response.
If you do not specify a value for [the path attribute], Navigator uses the path of the document that created the cookie property.
More formally, the W3C also weighs in on this issue in its cookie attribute entry in the DOM Level 2 HTML Specification:
If no domain attribute is specified, then the domain attribute for the new value defaults to the host portion of an absolute URI [IETF RFC 2396] of the current frame or document. If no path attribute is specified, then the path attribute for the new value defaults to the absolute path portion of the URI [IETF RFC 2396] of the current frame or document.

To look at it another way, if the domain and path attributes for a cookie are not set, they (are supposed to) default to values that limit the generality of the cookie - I leave it to others to decide if this is as it should be, or not.

For a cookie set at the "So, You Want To Set A Cookie, Huh?" page, whose URL is http://www.htmlgoodies.com/beyond/javascript/article.php/3470821, the "host name of the server which generated the cookie response" is www.htmlgoodies.com, whereas the "path of the document that created the cookie property" is /beyond/javascript/article.php. Consequently, www.htmlgoodies.com and /beyond/javascript/article.php are what dataCookie=oatmeal raisin's domain and path values respectively should be, and this is indeed the case with Firefox, Opera, and Safari, as can be verified via the Preferences... panes of these browsers, e.g.:

Parameter info for the dataCookie=oatmeal raisin cookie when using non-MSIE browsers

(The above screen shot was taken when using Safari. Note that the domain and path attributes for the other HTML Goodies cookies have been given the most general settings possible - .htmlgoodies.com and /, respectively - so that these cookies will be 'recognized' by every page at the HTML Goodies site.)

The URL of the demo page is http://www.htmlgoodies.com/legacy/beyond/javascript/cookie2.html; vis-à-vis the "So, You Want To Set A Cookie, Huh?" page, the demo page is served by the same server but its path is different. With respect to the demo page, a domain=www.htmlgoodies.com attribute passes domain matching, but a path=/beyond/javascript/article.php attribute does not pass path matching - see the "Determining a Valid Cookie" subsection of the "Netscape Cookies" Appendix - and therefore the non-MSIE browsers do not send the dataCookie=oatmeal raisin cookie to the www.htmlgoodies.com server when they request the demo page: that's why you don't see the oatmeal raisin value on the demo page when using Firefox, Opera, and Safari, which are behaving correctly; counterintuitively, it's actually Internet Explorer that is behaving incorrectly.

And how does MSIE handle a cookie's domain and path attributes? The MSDN Library's cookie property page tells you what happens when these attributes are set but, significantly, is silent on what happens when these attributes are not set; fortunately, this information can again be fished out of the browser Preferences... pane, which reveals that for the dataCookie=oatmeal raisin cookie,
(a) not setting the domain attribute causes domain to default to www.htmlgoodies.com - so far, so normal - but
(b) not setting the path attribute causes path to default to the more-general-than-it-should-be /:
Parameter info for the dataCookie=oatmeal raisin cookie when using MSIE
The dataCookie=oatmeal raisin cookie now passes both domain matching and path matching and is therefore sent to the www.htmlgoodies.com server when the demo page is requested, and that's why you see the oatmeal raisin value on the demo page when using MSIE.

The "So, You Want To Set A Cookie, Huh?" tutorial dates to a former 'incarnation' of HTML Goodies, in which the tutorial and demo page URL paths were likely identical and if so Joe could get away with not specifying a path attribute for the dataCookie cookie. As for the present incarnation of HTML Goodies, you have probably figured out that the tutorial demo's browser-specific behavior can be resolved by adding a path=/ parameter to the putCookie( ) document.cookie-writing statement:

document.cookie = cookie_name + "=" + YouEntered
+ "; path=/; expires=Monday, 04-Apr-2010 05:00:00 GMT";


Demo

I have crafted my own little shopping cart cookie demo that illustrates some aspects of the tutorial's scripts. In the div below, select a fruit item and then click the Add to Cart button.
(Your browser must be set to accept 'third-party' cookies for the demo to work.)

Welcome, shopper.

Please choose an item below:
Granny Smith apples, 4-lb bag: $5.00
Red seedless grapes, 2-lb basket: $5.00
Small navel oranges, 5-lb bag: $4.00

Like Joe's demo, my demo writes a cookie and generates a cookie-related output based on the user's input; however, my demo differs from Joe's demo in two key respects:
(1) In the "Welcome, shopper" div, I limit the user to three input choices.
(2) The "Please confirm your order" div prints out not the cookie value itself but a longer text string corresponding to that value.

N.B. My demo cookie does not specify an expires attribute - the cookie will be gone when you quit your browser.

In the following entry, we will revisit the topic of JavaScript slide shows with a brief look at HTML Goodies' "So, You Want a SlideShow, Huh?" tutorial.

reptile7

Saturday, May 09, 2009
 
Double-Cookied Script
Blog Entry #144

References
(1) The "Netscape Cookies" Appendix (C) of the JavaScript 1.3 Client-Side Reference
(2) Wikipedia's HTTP cookie page
(3-5) We previously discussed JavaScript cookies in Blog Entries #81, #82, and #83.

Ready for another go at a cookie script? Ah, I knew you were game. Today we'll check over HTML Goodies' "So, You Want To Set A Cookie, Huh?" tutorial, in which Joe offers a set-and-retrieve cookie script that he put together by heavily borrowing from the cookie script submitted by Giedrius. The original Giedrius cookie script is here; it's discussed in HTML Goodies' JavaScript Script Tips #60-64 and we went through it in Blog Entry #82. However, Joe does add a noteworthy twist to Giedrius' script: he places the script's 'set' and 'retrieve' parts on different pages so that the script's cookie more closely resembles a shopping cart cookie - this ends up causing problems for the script's demo, as we'll see later.

Set

Joe claims, Any time you set a cookie, you need to gather some information [from the user]: this is not generally true - you can of course set all of a cookie's values yourself if you want to, and most Web sites do just that when they set cookies - but it's certainly true for a shopping cart cookie. Joe's script sets a cookie whose name attribute value is dataCookie; for the cookie's value attribute value, the user is asked to enter a word into a cfd text box in a cf form

<form name="cf" action="">

Enter A Word: <input type="text" name="cfd" size="20" />

<input type="button" value="Set to Cookie" onclick="putCookie( );" />
</form>


and then click a button, triggering the putCookie( ) function in the script's 'set' part:

/* For the tutorial, Joe externalizes the following code (I've tweaked it a bit, as is my wont) here. */
var cookie_name = "dataCookie", YouEntered, index;
function putCookie( ) {
    if (document.cookie != document.cookie) index = document.cookie.indexOf(cookie_name);

    else index = -1;
    if (index == -1) {

        YouEntered = document.cf.cfd.value;

        document.cookie = cookie_name + "=" + YouEntered + "; expires=Monday, 04-Apr-2010 05:00:00 GMT"; } }


Per its identifier, the putCookie( ) function writes a cookie to the user's hard disk. In brief:

(1) The cookie's name value is dataCookie, which is assigned to a cookie_name variable prior to declaring putCookie( ).

(2) The user's text box input, document.cf.cfd.value, will be the cookie's value value, and is assigned to a YouEntered variable.

(3) cookie_name, an = sign, YouEntered, and a ; expires=Monday, 04-Apr-2010 05:00:00 GMT expires attribute string are concatenated to give the cookie, which is added to the browser's cookie collection via assignment to document.cookie (if the user has added the exact same cookie previously, the new cookie 'bumps' the old cookie - a duplicate cookie is not added).

Are you scratching your head at that if (document.cookie != document.cookie) { ... } conditional? Man, I sure was when I first saw it. In attempting to explain this code, Joe says in part:
The first thing we do is check to see if there even is a cookie. The line if (document.cookie != document.cookie) asks if the current cookie is not equal to the data entered by the user.

Writing that question actually saves a step. By asking if something is not equal to something else, I also ask if that something even exists. You see, if it doesn't exist, then it cannot be equal. Get it? I'm basically assuring that the cookie is rewritten each time the function is run.
Actually, if we want to know if there are any cookies associated with the current document (not necessarily set by the current document), then we should test therefor with an

if (document.cookie) { ... }

conditional à la Giedrius' script; to test if the user's input is present in the preexisting document.cookie string, an

if (document.cookie.indexOf(document.cf.cfd.value) != -1) { ... }

conditional would do the trick. The index = document.cookie.indexOf(cookie_name) statement in Joe's conditional would indirectly test if the dataCookie value is present in the document.cookie string: the index variable would return the index of dataCookie's starting d in document.cookie if it were and would return -1 if it weren't. However, the document.cookie != document.cookie condition does not in fact compare the preexisting document.cookie string with the user's input; rather, it compares the document.cookie return with itself, and necessarily returns false. As a result, index is set to -1 regardless of whether document.cookie does or does not contain a dataCookie cookie.

Joe evidently wants to use the index variable to flag first-time visitors (index = -1) vs. return visitors (0index < document.cookie.length-10) as Giedrius' script does. But for a shopping cart cookie, we don't really care if the user is a first-time or return visitor in the sense that the user should be able to overwrite the dataCookie cookie again and again if desired; consequently, we could easily shrink the putCookie( ) function to

function putCookie( ) {
    YouEntered = document.cf.cfd.value;

    document.cookie = cookie_name + "=" + YouEntered + "; expires=Monday, 04-Apr-2010 05:00:00 GMT"; }


without any problems. BTW, contra the tutorial, only the ; expires=Monday, 04-Apr-2010 05:00:00 GMT string in the putCookie( ) document.cookie statement needs to be "on one line"; the statement's other operands, and its operators, could be put on separate lines if for some reason you wanted to do that.

Before moving on:
Simply replacing the document.cookie != document.cookie condition with a document.cookie condition would not allow a return visitor to overwrite the dataCookie cookie; again, we are better off getting rid of the index testing code in this regard.

Fetch

So, we type something into the "Enter A Word" field. Joe correctly points out that a cookie value should not contain white space; it shouldn't contain commas or semicolons either. However, the browsers on my computer - including not only OS X browsers but also Netscape 4.61 and MSIE 4.5 in the SheepShaver environment - allow me to set with Joe's script a cookie value comprising more than one word without having to escape its space characters (with %20s or +s), e.g., I can write a dataCookie=oatmeal raisin cookie, which we'll use for the discussion below.

We click the button and then follow the Click to go to another page link to the tutorial's demo page, whose source contains the script's 'retrieve' part:

var cookie_name = "dataCookie", YouWrote, index, namestart, nameend;
function getName( ) {

    if (document.cookie) {
        index = document.cookie.indexOf(cookie_name);
        if (index != -1) {

            namestart = document.cookie.indexOf("=", index) + 1;

            nameend = document.cookie.indexOf(";", index);
            if (nameend == -1) nameend = document.cookie.length;

            YouWrote = document.cookie.substring(namestart, nameend);

            return YouWrote; } } }


YouWrote = getName( );
if (YouWrote == "dataCookie") YouWrote = "Nothing_Entered";


Joe pads this code out somewhat for the demo page, as follows:

(1) Prior to the getName( ) function, Joe adds a testCookie( ) function that
(a) uses a while loop to search for dataCookie in the document.cookie string; if dataCookie is present, testCookie( ) then
(b) uses indexOf( ) and substring( ) commands to extract the dataCookie value, which is returned and printed out on the second line of the demo page display -
at least that's what's supposed to happen. In practice, the aforementioned while loop contains an uppercase-for-lowercase error

end = start + name.Length; // should be: end = start + name.length;

that prevents testCookie( ) from working/returning properly.

I originally wasn't going to put the testCookie( ) function in front of you (I view it as excess baggage, and it could be written in a lot fewer lines of code anyway), but maybe I should do so - testCookie( ) and its calling expression appear in the div below:

function testCookie(name) {
    var cookieFound = false;
    var start = 0;
    var end = 0;
    var cookieString = document.cookie;
    var i = 0;
    while (i <= cookieString.length) {
        start = i;
        end = start + name.Length;
        if (cookieString.substring(start,end) == name) { 
            cookieFound = true;
            break; } 
        i++; }
    if (cookieFound) {
        start = end + 1;
        end = document.cookie.indexOf(";",start);
        if (end < start) {
            end = document.cookie.length; }
        return document.cookie.substring(start,end); }
    else {
        return "No cookie matching was found"; } }
testcx = testCookie("dataCookie");

(2) In the getName( ) function, the if (index != -1) { ... } block is followed by an

else return "No cookie name found." + Test;

statement in case dataCookie is not found in document.cookie. The Test variable is declared locally in the getName( ) function

var Test = cookie_name;

on the line following the if (document.cookie) declaration and (like cookie_name) evaluates to dataCookie.

(3) The getName( ) function concludes with an

else return "No cookie ID found.";

statement in case there are no cookies associated with the current document.

For a dataCookie=oatmeal raisin cookie, here's what we should see on the demo page:

You Entered oatmeal raisin.
oatmeal raisin
You Entered:
<<...Back

(The display is actually double-spaced, via p elements.)

The display's first three lines are respectively coded by the following source commands:
(1) document.write("You Entered " + YouWrote + "."); /* As shown above, YouWrote is the getName( ) return. */
(2) document.write(testcx); // Similarly, testcx is the testCookie( ) return.
(3) document.write("You Entered: <input type='text' size='30' value='" + YouWrote + "' />");
/* In the preceding command, YouWrote must be surrounded by single quotes; otherwise, you'll only see oatmeal in the text box. */


Excluding the <<...Back link, the demo page display when using Internet Explorer is

You Entered oatmeal raisin.
You Entered:

if the text box value remains unquoted per the demo source. MSIE detects the dataCookie=oatmeal raisin cookie and the demo page does at least return oatmeal raisin for YouWrote. The display's second line 'disappears' because, to make a long story short, the testCookie( ) document.cookie.substring(start, end) command, in which the start and end parameters evaluate to NaN due to the L-for-l error discussed above, returns an empty string.

In brief, here's how getName( ) extracts the oatmeal raisin value from the dataCookie=oatmeal raisin cookie:

(1) First, the index of the beginning o of oatmeal raisin in the document.cookie string is located with
namestart = document.cookie.indexOf("=", index) + 1;
this index is assigned to the variable namestart.

(2) We then locate the index one past that of the final n of oatmeal raisin in document.cookie with either
nameend = document.cookie.indexOf(";", index);
if dataCookie=oatmeal raisin is not the last document.cookie cookie, or
if (nameend == -1) nameend = document.cookie.length;
if dataCookie=oatmeal raisin is the last document.cookie cookie (in this case, the index corresponds to the null character position in a C string); this index is assigned to the variable nameend.

(3) Finally, we return and assign to YouWrote the oatmeal raisin subset of the document.cookie string with
YouWrote = document.cookie.substring(namestart, nameend);.

Curiously, for the most recent version of MSIE that I have on my computer - that would be MSIE 5.2.3 for Mac OS X - no domain=.htmlgoodies.com-type cookies are set when I visit the "So, You Want To Set A Cookie, Huh?" tutorial (or any other HTML Goodies page), and dataCookie=oatmeal raisin is the only cookie in the document.cookie string; in getName( ), index, namestart, and nameend thus have the values 0, 11, and -1, respectively.

FYI: The 'retrieve' script's final
if (YouWrote == "dataCookie") YouWrote = "Nothing_Entered";
conditional only comes into play if the user were to actually enter dataCookie into the cfd field, and not if nothing is entered, as incorrectly stated in the tutorial; in the latter case, the cookie value would not be the name of the cookie but an empty string.

So far, so good - at least when using Internet Explorer. But things go south with other browsers, as we'll see in our next episode.

reptile7


Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)