reptile7's JavaScript blog
Tuesday, December 26, 2006
 
Per Second, Every Second
Blog Entry #61

Way back in HTML Goodies' JavaScript Primers #3, we used the core JavaScript Date object and several of its methods to create a static display of the time at which a user visits a Web page. Complementarily, we examine in this entry a script that uses the Date object and its methods to create a second-by-second running time display - a digital clock in effect. The key to the script, as we'll see later, is a recursive function, by now an established part of our JavaScript arsenal; recall, for example, that we employed a recursive function in Blog Entry #45 to reconfigure the animation script of Primer #28.

(HTML Goodies' recently posted "JavaScript Basics Part 12" article is about recursion, and provides some examples thereof but not a running clock example.)

Today's script is discussed over the course of HTML Goodies' JavaScript Script Tips #25, #26, #27, and #28 and is posted here. The Script Tips #25-28 Script is actually the first of three digital clock scripts that are covered in the Script Tips: an image-based digital clock script spans Script Tips #84-86, and a more elaborate and interesting time zone clock script spans Script Tips #87-90.

The Script Tips #25-28 Script is loaded into a div below for your convenience:

<script language="javascript">

function RunningTime( ) {

var RightNow = new Date( );

var ampm = RightNow.getHours( );
if (ampm >= 12) {nampm = "PM";}
else {nampm = "AM";}

var hr = RightNow.getHours( );
if (hr > 12) {nhr = hr - 12;}
else {nhr = hr;}

if (hr == 0) {nhr = "12";}
else {nhr = nhr;}

var min = RightNow.getMinutes( );
if (min < 10) {nmin = "0" + min;}
else {nmin = min;}

var sec = RightNow.getSeconds( ) + 1;

if (sec < 10) {nsec = "0" + sec;}
else {nsec = sec;}

if (nsec >= 60) {nnsec = "00";}
else {nnsec = nsec;}

var printIt = "Time: " + nhr + ":" + nmin + ":" + nnsec + ":" + nampm;
document.clock.clockface.value = printIt;

var KeepItGoing = setTimeout("RunningTime( );","1000"); }

</script>

<form name="clock">
<input type="text" name="clockface">
<input type="button" value="Get the Time" onclick="RunningTime( );">
</form>

As demonstrated in Script Tips #25-28, the script is set up to display a "Time: hour:minute:second:XM" string in an <input type="text"> box upon clicking a "Get the Time" push button. (In Script Tips #25 and #28, Joe 'apologizes' for not executing the script via a <body> onload attribute so that the clock is already up and running when the page has loaded, but I myself think it's kind of cool to make the clock appear with the click of a button.)

In turn, the running parts of the clock string reflect variable values that are generated by the script's RunningTime( ) function, which is called by the "Get the Time" button:

Time: nhr:nmin:nnsec:nampm
nhr is a 'traditional' 1-to-12 hour value
nmin is a 2-digit 00-to-59 minute value
nnsec is a 2-digit 00-to-59 second value
nampm is an AM or PM string, as appropriate

RunningTime( )'s use of variables and if...else statements is a bit of a mess, however - the word "shambolic" comes to mind. Two variabilizations of the new Date( ).getHours( ) return? else {nhr = nhr;}?? I can't let this stand! We can spruce up Joe's RunningTime( ) code via a simple strategy:
(a) set up 'default' values for the hour, minute, second, and AM/PM parts of the clock string, and
(b) use if statements to set nondefault values as needed;
as shown below, this will allow us to remove all six of RunningTime( )'s else statements.

The getHours( ) return

In Script Tip #26, Joe correctly notes that the getHours( ) return runs from 0 (12 AM) to 23 (11 PM). If we want the hour part of the clock string to run from 1 to 12, then the 1-to-12 subset of the getHours( ) return can be left alone. All but one of the 1-to-12 getHours( ) values are "AM" - only the 12 getHours( ) value is "PM" - so let's choose the 1-to-11 getHours( ) values and AM as our defaults for the nhr and nampm variables, respectively. We can then use three if statements to address the 'special needs' of the 12, 0, and 13-to-23 getHours( ) values:

var RightNow = new Date( );
var nhr = RightNow.getHours( );
var nampm = "AM";
if (nhr == 12) nampm = "PM"; // if statement #1
if (nhr == 0) nhr = 12; // if statement #2
if (nhr > 12) {nhr = nhr - 12; nampm = "PM";}
/*It's important that if statement #1 precedes if statement #2; otherwise, the clock will display PM during the midnight-to-1-AM hour.*/

And that's it! My code is actually not that much different than Joe's code, but again, it's simply not necessary here to create new variables and include else branches.

The getMinutes( ) and getSeconds( ) returns

The minute and second parts of the clock are even easier to deal with. Both the getMinutes( ) and getSeconds( ) returns run from 0 to 59. If we want the minute/second parts of the clock string to display two digits and run from 00 to 59, then the 10-to-59 subsets of the getMinutes( )/getSeconds( ) returns can be left alone and will serve as our defaults for the nmin and nnsec variables. We can then use two if statements to prefix a 0 to the 0-to-9 getMinutes( )/getSeconds( ) values:

var nmin = RightNow.getMinutes( );
if (nmin < 10) nmin = "0" + nmin;
var nnsec = RightNow.getSeconds( );
if (nnsec < 10) nnsec = "0" + nnsec;

Joe addresses the minute and second parts of the clock in Script Tip #27. For reasons utterly beyond my understanding, he adds one to the getSeconds( ) return

var sec = RightNow.getSeconds( ) + 1;

and then corrects therefor (i.e., for an nsec value of 60) with the following code:

if (nsec >= 60) {nnsec = "00";}
else {nnsec = nsec;}

Joe somehow leaves the "+1" out of his discussion, and then says, One of the strangest things about the seconds return is that when the second 'turns over' to count again, it always starts at 1. That means '60' is the highest number. This is true in this case only because of the "+1", but as noted above, the unalterated getSeconds( ) return peaks out at 59 and then "turns over" to 0. So there's no need to add 1 and subtract 60 here.

Displaying the clock

Let's put it all together. Once properly set, the nhr, nmin, nnsec, and nampm variables are delimited with colons (not semicolons, Joe) and prefixed with a "Time: " string, and the whole thing is assigned to the variable printIt. Subsequently, the script displays printIt by assigning it to the value of the <input type="text" name="clockface"> box in the document body.

Because the clockface field and the "Get the Time" button are not successful controls, we can alternately
(a) remove their containing form element,
(b) replace the name="clockface" attribute with an id="clockface" attribute, and
(c) display the clock with a document.getElementById("clockface").value = printIt statement
if you'd rather do that.

Last and certainly not least, the

var KeepItGoing = setTimeout("RunningTime( );","1000");

command enables the clock display to update on a second-by-second basis by recursively calling the RunningTime( ) function after a 1000-millisecond time delay. In Script Tip #28, Joe makes some comments about the setTimeout( ) method that need to be clarified:

(1) setTimeout( ) is a command that basically makes the script wait a specified amount of time. The setTimeout( ) method does delay the execution of its first-parameter expression, but it does not stall any subsequent script commands; in the code below, for example, the setTimeout( ) command would not delay the alert( ) box display by 1000 milliseconds:

window.setTimeout("RunningTime( );",1000);
window.alert("Hello, World!");
// The second (millisecond) setTimeout( ) parameter does not need to be quoted.

(2) I always set up my setTimeout( ) statements as variables. Why? That's the way I learned it. That's the way I've always done it. In reality you don't need the var varname =. FYI, variabilization of a setTimeout( ) command comes into play when the setTimeout( ) time delay is canceled with a window.clearTimeout( ) command:

var timeDelay = window.setTimeout("RunningTime( );",1000);
window.clearTimeout(timeDelay);

(3) The stuff inside the parentheses are 'parameters' that the setTimeout is to work upon. The format is to first list the function name that will run. It is always the function name that the setTimeout( ) is sitting within. Always? The setTimeout( ) method's first parameter does not need to be a function, recursive or otherwise, but can be any executable JavaScript expression, e.g.:

window.setTimeout("document.bgColor='blue';",5000);

We'll see another time-related application of the Date object in the next post when we study the Script Tips #29-30 Script, which calculates the number of days spanning two points in time.

reptile7

Labels:


Sunday, December 17, 2006
 
Thanks for Stopping By
Blog Entry #60

Script Tips #21, #22, #23, and #24 discuss a simple guestbook script that
(a) provides an input box for user comments and then
(b) pops up an acknowledgement message in a new window when the user's comments are submitted via email to the script's addressee, whoever it might be. At no extra charge, the acknowledgement message incorporates the user's name and email address, which are collected by two prompts at the beginning of the script, and also the user's comments. The Script Tips #21-24 Script appears here and has a demo page here, and is reproduced below:

<script language="javascript">
var name = prompt("What is your name?","Write It Here.");
var email = prompt("What is your email address?", "Write It Here.");
</script>
<script language="javascript">
function verify( ) {
var OpenWindow = window.open("", "newwin", "height=300,width=300");
OpenWindow.document.write("<html>");
OpenWindow.document.write("<title>Thanks for Writing</title>");
OpenWindow.document.write("<body bgcolor='ffffcc'>");
OpenWindow.document.write("<center>");
OpenWindow.document.write("Thank you <b>" + name + "</b> from <b>" + email + "</b><p>");
OpenWindow.document.write("Your message <p><i>" + document.gbookForm.maintext.value + "</i><p>");
OpenWindow.document.write("from " + name + " / " + email + "<p>");
OpenWindow.document.write("will be sent along when you close this window.<p>");
OpenWindow.document.write("<center>");
OpenWindow.document.write("<form><input type='button' value='Close Window' onclick='self.close( );'></form>");
OpenWindow.document.write("</center>");
OpenWindow.document.write("</html>"); }
</script>
<form method='post' action='mailto:jburns@htmlgoodies.com?Subject=Script Tip mail from " + name + " at " + email + "' enctype='text/plain' name='gbookForm'>
<b>What would you like to tell me?<br></b>
<textarea cols="40" rows="20" name="maintext"></textarea><p>
<input type="submit" value="Send It" onclick="verify( );">
</form>

As shown above, the Script Tips #21-24 Script contains two script elements. The first script element holds two prompt( ) commands that solicit the user for a name and email address and then assign the user's inputs to the variables name and email, respectively. (Although both name and email strike me as poor choices for variable names, neither of them is a JavaScript reserved word.) The second script element codes the acknowledgement message window/document à la the Primer #12 Script. However, check the source code of the demo page document, which contains a third script element for the start-tag of the form element just below the second script element - we'll have more to say about this later.

The textarea element

The "Forms" section of the HTML Goodies site sports a "So, You Want A Guestbook, Huh?" tutorial with a sample guestbook comprising two <input type="text"> boxes, a set of five radio buttons, a textarea box, and submit and reset buttons. The Script Tips #21-24 Script guestbook is simpler; it substitutes the two prompt( ) commands for the <input type="text"> boxes and then features only a textarea box and a submit button. I thought we might discuss textarea boxes a bit, given the central roles they play in these and other guestbooks and also because, as far as form controls go, I've heretofore given them pretty short shrift on this blog.

Some references:
(1) The textarea element and its cols and rows attributes are detailed here in the W3C's HTML 4.01 Specification.
(2) The JavaScript 1.3 Client-Side Reference treats the textarea object here.
(3) In the Document Object Model (DOM) Level 2 HTML Specification, the HTMLTextAreaElement Interface appears here.

A textarea element is like an <input type="text"> element but allows multiline input. The width and height of a textarea box can be set by the textarea cols and rows attributes, respectively; the cols value corresponds roughly to the number of characters in a monospaced font that can be entered per line before either horizontal scrolling or wrapping to the next line becomes necessary, whereas the rows value sets the number of text lines that can be entered per box before vertical scrolling becomes necessary. For example,

<textarea cols="60" rows="6"></textarea>

creates a box whose visible display is 60 characters across, 6 rows down. The W3C does not specify default values for the cols and rows attributes, saying merely that they are "required." I find on my iMac that a textarea element without cols and rows attributes gives a box about 20 characters across and 2 (MSIE) or 3 (Netscape) rows down.

The value of a textarea element corresponds to its contents; unlike the <input> tag, the <textarea> tag does not take a value="whatever" attribute:

<textarea>Hello, World!</textarea>
as opposed to
<input type="text" value="Hello, World!">

However, a document.formName.controlName.value or document.getElementById("controlID").value expression can be used to read/write the value of either a textarea element or an <input type="text"> element.

We can use CSS to style a textarea element's contents and also its borders:

<style type="text/css">
textarea {color: red; font-weight: bold; border-style: dashed;}
</style>



Relatedly, a textarea element's contents cannot ordinarily be formatted with HTML markup*:

<textarea><i>These words are italicized</i></textarea>

gives a box that literally displays <i>These words are italicized</i> and not These words are italicized. However, textarea elements do render HTML character references normally, e.g., <textarea>&pound;100</textarea> gives a box that displays £100. It follows that &#10; - the numeric character reference for a line feed, and equivalent to \n - but not <br>, can be used to force a line break in a textarea box.

(*This is because the textarea element, as defined in the W3C's HTML 4.01 ("Strict") Document Type Definition (DTD), has a #PCDATA content model - "#PCDATA" and "content model" are briefly defined here. I encourage you to read through the Text Encoding Initiative's "A Gentle Introduction to SGML", which is quite helpful in making sense of the W3C's HTML DTD information. SGML, the Standard Generalized Markup Language, is the metalanguage that 'gave birth' to HTML; unfortunately and annoyingly, the SGML specification is not online.)

Nevertheless, a textarea element can be engineered to contain/render other HTML elements - e.g., go to a "comments" page of a Blogger.com blog and check out the "Leave your comment" textarea box, in which "[y]ou can use some HTML tags" - but this topic is beyond the scope of this blog entry.

The form element and its submission

So, the user enters one or more comments into the textarea box and clicks the "Send It" submit button. What happens then? We turn now to the guestbook form's <form> start-tag:

<form method='post' action='mailto:jburns@htmlgoodies.com?Subject=Script Tip mail from " + name + " at " + email + "' enctype='text/plain' name='gbookForm'>

Let's take it one attribute at a time.

method='post': this probably means that the form will be sent to its processing agent via the HTTP POST method, technically defined here, i.e., the user's comments will be transmitted as a separate data block and not appended to the URL specified by the form action attribute as would occur if method="get". However, the W3C notes here that if the form action attribute value is not an HTTP URL (and it is not in this case), then [user agent] behavior is unspecified.
(N.B. Browsers are user agents, but not all user agents are browsers.)

action='mailto:jburns@htmlgoodies.com?Subject=Script Tip mail from " + name + " at " + email + "': this value of this attribute is a mailto URL to which the form is sent, in this case Joe's email address augmented by a query string specifying a "Subject" header and incorporating the name and email variables.

If the quote formatting of the action value seems strange to you, well, it did to me too initially; upon checking the source code of the Script Tips #21-24 demo page, however, it seems that the guestbook form's <form> start-tag is meant to be written by a document.write( ) command:

document.write("<form method='post' action='mailto:jburns@htmlgoodies.com?Subject=Script Tip mail from " + name + " at " + email + "' enctype='text/plain' name='gbookForm'>");
// Ah, much better, eh?

There's just one little problem we need to mention before moving on: modern browsers do not support (more specifically, will not submit to the processing agent) action="mailto:addressee@some_domain.com" forms, a point made by HTML Goodies in this undated article. On my computer - and I am not exactly a 'cutting-edge user' - neither MSIE 5.1.6 nor Netscape 7.02 will submit the gbookForm form, but Netscape 4.79 will! The HTML Goodies "Forms" section provides links to several tutorials that offer more state-of-the-art methods for processing email forms - methods involving the use of PHP, CGI/Perl, and ASP scripts - but I am not conversant with these technologies and for now will leave you to your own devices on this front.

In continuing, let's assume that the user's browser is able to submit the gbookForm form using an action mailto: URL.

enctype='text/plain': Joe discusses this attribute/value here; without it, the user's comments will be encoded by the browser, and received by the addressee, according to the application/x-www-form-urlencoded content type, for example:

If the user types in
I'm really impressed with your Web page!
then without enctype='text/plain' the addressee will see
maintext=I%27m+really+impressed+with+your+Web+page%21

Having said this, the W3C warns that user agent behavior for enctype values other than application/x-www-form-urlencoded and multipart/form-data is "unspecified."

Looking over the aforementioned HTML Goodies email forms tutorials and following the links therein:
(1) It would seem that if the gbookForm form were sent to a CGI/Perl or ASP processing agent, then the enctype='text/plain' attribute is unnecessary and can/should be left out**; presumably, these processing agents are able to unescape the application/x-www-form-urlencoded encoding.
(2) As for a PHP processing agent, the <form> start-tag in the HTML of "Jack's FormMail.php" holds an enctype="multipart/form-data" attribute, but note that the form contains <input type="file"> elements for uploading files, for which this enctype value is necessary.

(**Moreover, EarthLink offers me the use of a CGI mailto script with accompanying HTML whose form element does not have an enctype attribute.)

name='gbookForm': self-explanatory at this point, yes?

Other submit possibilities?

(1) The Script Tips #21-24 Script's <input type="submit"> element should be replaceable by a button element, if preferred:

<button onclick="verify( );">Send It</button>
<!--Note that type="submit" is the default.-->

Alas, my plan to try out the script with a button element was thwarted by Netscape 4.79's nonsupport for the button element (which was introduced in HTML 4.0, and Netscape did not provide full support for HTML 4 prior to Netscape 6).

(2) My first idea for sorting out the quote formatting of the gbookForm form action attribute was to
(a) remove the action attribute from the <form> start-tag,
(b) recode the <input type="submit"> button as a generic <input type="button"> push button, and
(c) insert the following two commands at the beginning of the verify( ) function in the script's second script element:

document.gbookForm.action = "mailto:jburns@htmlgoodies.com?Subject=Script Tip mail from " + name + " at " + email;
document.gbookForm.submit( );

However, Netscape's submit( ) method documentation warns, The submit method fails without notice if the form's action is a mailto:, news:, or snews: URL, and this is precisely what happened (the gbookForm form was not sent) when I tried to put this idea into practice.

The verify( ) function

Clicking the submit button also triggers the second script element's verify( ) function, which pops up an acknowledgement message for the user in a new window. As noted above, we did the coding-a-new-window-in-an-opener-document thing in Primer #12, so I'm only going to offer a few 'aesthetic' comments here.

• If desired, the
OpenWindow.document.write("<html>") and OpenWindow.document.write("</html>")
commands can be removed; the W3C notes here that the html element's start-tag and end-tag are in fact optional.

• On my computer, I find that the presentational attributes of the OpenWindow document body can be set with style commands if the <body> start-tag is left in place, even as the body element's start-tag and end-tag are also supposed to be optional:

// Replace the <body bgcolor='ffffcc'> and center element commands with:
OpenWindow.document.write("<body>");
OpenWindow.document.body.style.backgroundColor = "#ffffcc";
OpenWindow.document.body.style.textAlign = "center";
// These style commands do not work with Netscape 4.79.

The "Control types" section of the HTML 4.01 Specification notes:
"The elements used to create controls generally appear inside a FORM element, but may also appear outside of a FORM element declaration when they are used to build user interfaces...Note that controls outside a form cannot be successful controls [i.e., their name/value information cannot be sent to a form processing agent]."
Clearly, the button in the acknowledgement window is not meant to be a "successful control" (this control doesn't even have a name) and is merely part of the "user interface"; it follows that this button's containing form element is unnecessary:

OpenWindow.document.write("<input type='button' value='Close Window' onclick='self.close( );'>");
// Netscape 4.79 does not render controls lacking a form element parent.

Smaller to bigger

We wrap up on a minor technical point. In Script Tip #23, Joe states, In the hierarchy of JavaScript, any hierarchy statement goes from biggest to smallest, moving left to right. This is almost always but not %100 true, and form controls provide a notable exception in this regard. Each form control object has a form property that returns a reference to the control's parent form object (it returns null if the control is outside of a form); relevant examples involving the text object are detailed here in the JavaScript 1.3 Client-Side Reference for those who are interested.

Tick, tick, tick...next up: a digital clock script spanning Script Tips #25-28.

reptile7

Labels:


Monday, December 04, 2006
 
In Any Colour You Like
Blog Entry #59

HTML Goodies' JavaScript Script Tip #19 is titled "Using JavaScript To Create HTML [T]ags," and we'll do just that in this post as we discuss a script that custom-builds a <body> tag and consequently sets on-the-fly two global document object properties:
(a) the document background color, and
(b) the document text color, a.k.a. the document foreground color.
(We've set the document background color via document.bgColor statements several times previously - see the Primer #17 Script, for example - but this'll be the first time that we can choose our own color value.)

Our script du jour spans Script Tips #19 and #20, is demonstrated by Joe here, and is detailed below:

<html><title>Title text</title>
<script language="javascript">
var color = prompt("What color would you like the page's background to be?","");
var txtcolor = prompt("What color would you like the text to be?","");
document.write("<body bgcolor=" + color + " text=" + txtcolor + ">");
defaultStatus = "Here's your " + color + " background and " + txtcolor + " text.";
</script>
Page text...
</html>

By design, the script's script element necessarily straddles the document head and body; the script contains no <head>/</head> tags, which are in any case optional.

The script's process is quite straightforward.
(1) A first prompt( ) box pops up, soliciting the user for a document background color choice; the user types in a color, which is assigned to the variable color after the user clicks the prompt( ) box's button.
(2) A second prompt( ) box pops up, soliciting the user for a document text color choice; the user types in a color, which (after clicking OK) is assigned to the variable txtcolor.
(3) Subsequently, color and txtcolor are plugged into a document.write( ) command that writes the document's <body> tag; color and txtcolor serve as the values for the body element's bgcolor and text attributes, respectively.
(4) Finally, color and txtcolor are concatenated with three short text strings to form a message that is written to the browser window's status bar.

For the two prompts, the user can enter a 'natural language' color - white, blue, etc.; see HTML Goodies' indispensable "So, You Want A Basic Color Code, Huh?" for possible values in this regard - or a 3-digit or 6-digit RGB code or even an rgb( ) function, if desired.

Of course, we can further build up the <body> tag via additional prompts that collect values for other body element attributes: link, vlink, etc. More generally, it should also be clear that we can apply the script's tag-construction methodology to an <img> tag, an <input> tag, or any other HTML tag. The overall approach here is reminiscent of Primer #12, in which we assembled from scratch an HTML document in a newly opened window by a series of document.write( ) commands; note that the Primer #12 Assignment similarly uses a prompt( ) output to custom-build the content of a title element.

Don't leave it blank, part 3

"What happens if the user leaves the prompt( ) boxes blank, doesn't fill in any colors, and then clicks 'OK' and/or 'Cancel'?"

Somehow I knew you'd ask that...well, what would you think? Let's first consider the blank-OK/blank-OK possibility. In this case, color and txtcolor are outputted as empty strings, so when we plug them into the document.write( ) command, the <body> tag in effect becomes:

<body bgcolor="" text="">

The document's background and foreground colors should then default to those set by the browser's Preferences pane (typically black text on a white background), and this is indeed what happens if we tighten up the <body> tag code. The body element attribute values should be quoted but are not, so let's add some single quotes:

document.write("<body bgcolor='" + color + "' text='" + txtcolor + "'>");

Without the single quotes, the blank-OK/blank-OK input on my iMac gives, for reasons beyond my understanding, a black page when using either MSIE 5.1.6 or Netscape 7.02; the returns for document.bgColor and document.fgColor are #0e0000 and #000000, respectively.

We learned in Blog Entry #11 that clicking the button on a prompt( ) box outputs the primitive value null. I observe that MSIE and Netscape treat a color value of null differently. MSIE evidently ignores the null value in this instance and gives the black-text-on-white-background default in response to blank-OK/blank-Cancel, blank-Cancel/blank-OK, and blank-Cancel/blank-Cancel inputs. In contrast, Netscape converts null to 0 and gives
(a) a black page in response to the blank-Cancel/blank-OK and blank-Cancel/blank-Cancel inputs, or
(b) the black-text-on-white-background default in response to the blank-OK/blank-Cancel input.
Netscape's behavior is consistent with the JavaScript 1.5 Core Guide's "Evaluating Variables" section, which states, When you evaluate a null variable, the null value behaves as 0 in numeric contexts and as false in Boolean contexts.

It is left to the reader to craft if statements that intercept and deal with these possibilities.

Customizing a body onload attribute

In Script Tip #20, Joe says:
"Up until now you might have been used to putting text into the status [bar] by a JavaScript Event Handler, then window.status. Well, you don't always have such an event to attach a window.status statement to. This is one of those cases...[The defaultStatus statement] can be plopped basically anywhere and whatever follows it shows up in the status bar."
However, I noted here in Blog Entry #18 that when using MSIE I am unable to write the window.defaultStatus or window.status property unless I assign it to an event handler. To see the status bar effect of the Script Tips #19-20 Script with MSIE, then, it is necessary that I coordinate the defaultStatus statement with the page-loading process via a suitable body onload attribute. Coding a customizable onload attribute is trickier than coding the corresponding bgcolor and text attributes but is doable:

<script type="text/javascript">
var color = window.prompt("What color would you like the page's background to be?");
var txtcolor = window.prompt("What color would you like the text to be?");
var colormessage = "Here&#39;s your " + color + " background and " + txtcolor + " text.";
document.write("<body bgcolor='" + color + "' text='" + txtcolor + "' onload='window.defaultStatus=\"" + colormessage + "\";'>");
</script>

As shown above, to get this to work, I had to escape
(a) the Here's apostrophe in the status bar message with the &#39; numeric character reference, and
(b) the double quotes surrounding the colormessage value; either \", &quot;, or &#34; can be used for this purpose.
But work it does with both MSIE and Netscape.

As for that demo page...

The Script Tips #19-20 Script demo page is interesting for a couple of reasons.

(1) Looking at the demo page's source code, we see that the demo document contains a nobr element. Never heard of it? Me neither. And you won't find it on the W3C's HTML 4.01 Specification "Index of Elements" page. It turns out that the nobr element is a 'proprietary' Microsoft (but also recognized by Netscape) HTML extension, and its function is opposite that of the br element, i.e., the nobr element forces multiline content to not wrap but to extend indefinitely to the right (presumably it would prevent wrapping on the left for a right-to-left language), e.g.:

Congress shall make no law respecting an establishment of religion, or prohibiting the free exercise thereof; or abridging the freedom of speech, or of the press; or the right of the people peaceably to assemble, and to petition the Government for a redress of grievances.

Getting back to the demo page source, there's no need to prevent any of the six printed lines of the nobr element from wrapping - none of them is long enough to wrap, anyway - and Joe himself breaks up the nobr element with no fewer than five br elements, thus killing the nobr effect; it follows that the nobr element here serves no purpose and should be removed.
(I'm not sure why anyone would want to use the nobr element to structure text in the first place, speaking as someone who would like to see horizontal scrolling go the way of smallpox and the dinosaurs.)

(2) The demo page behaves as expected when using Netscape but not when using MSIE, at least on my computer. With MSIE, both script prompt( ) boxes pop up, but regardless of what I enter (or don't enter) into them, the resulting page displays blue (#0000c0) text on a black (#0c0000) background. With a little detective work, I was able to source the problem to the nonscript, printed part of the page, specifically the 4th printed line of the nobr element:

<br>document.write("<body BGCOLOR=" +color+ " TEXT=" +txtcolor+ ">")

When the HTML parsers of Netscape and MSIE hit the <body character sequence, then they begin writing a new body element. Subsequently, Netscape is able to recognize the color and txtcolor prompt( ) outputs and renders the page normally, whereas MSIE does not recognize color and txtcolor and the resulting new <body> tag is:

<body bgcolor=" +color+ " text=" +txtcolor+ ">

I have no clue as to how MSIE converts illegal color values to legal color values. In any case, the creation of the new body element can be easily stopped by escaping the angle brackets in the document.write( ) command:

document.write("&lt;body bgcolor=" +color+ " text=" +txtcolor+ "&gt;")

This simple change allows MSIE to render the demo page without incident.

A style-based script

If you've visited my links above for the bgcolor, text, link, and vlink attributes of the body element, then you probably noticed that all of these attributes are, uh, deprecated, and I'm sure you know what that means: according to "those who run the coding show" (Joe's description of the W3C), they should be replaced by equivalent style information. Fortunately, applying CSS to the Script Tips #19-20 Script is not at all difficult. The HTML body bgcolor attribute maps onto the CSS background-color property, whereas the HTML body text attribute maps onto the CSS color property. We can therefore retool the script's document.write( ) command with a body style attribute as follows:

var color = window.prompt("What color would you like the page's background to be?");
var txtcolor = window.prompt("What color would you like the text to be?");
document.write("<body style='background-color: " + color + "; color: " + txtcolor + ";'>");

Alternatively, we could use a header style element; from

<style type="text/css">
body {background-color: <color>; color: <color>;}
</style>

we obtain:

var color = window.prompt("What color would you like the page's background to be?");
var txtcolor = window.prompt("What color would you like the text to be?");
document.write("<style type='text/css'>");
document.write("body {background-color: " + color + "; color: " + txtcolor + ";}");
document.write("</style>");

A second style-based approach

When I first looked at the Script Tips #19-20 Script, it seemed unnatural to me. I thought, "Why are we using JavaScript to write an HTML tag? Why should JavaScript muscle in on HTML's turf? Aren't JavaScript and HTML supposed to complement each other? Why don't we use a more purely JavaScriptic method to achieve the script's color effects?" And of course, classic (pre-DOM) JavaScript provides bgColor and fgColor document object properties for this very sort of thing. However, Mozilla's DOM Reference notes that the document.bgColor and document.fgColor expressions are now deprecated and should be updated to document.body.style.backgroundColor and document.body.style.color, respectively. Application thereof to the Script Tips #19-20 Script gives:

<script type="text/javascript">
var color = window.prompt("What color would you like the page's background to be?");
var txtcolor = window.prompt("What color would you like the text to be?");
document.body.style.backgroundColor = color;
document.body.style.color = txtcolor;
window.defaultStatus = "Here's your " + color + " background and " + txtcolor + " text.";
</script>

Try it out below:



Here's some document body text.

If you're using a modern browser (as I trust you are), then the defaultStatus assignment probably won't work for you - actually, your browser may not even have a status bar - so I'll also print out your color choices below.

Background color:
Text color:

In the next entry we'll check out an old-school guestbook script spanning Script Tips #21-24.

reptile7


Powered by Blogger

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