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"); }


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

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);

(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.:


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.



Comments: Post a Comment

<< Home

Powered by Blogger

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