reptile7's JavaScript blog
Tuesday, March 22, 2016
 
Your id, Señor Clock
Blog Entry #363

Our tour of the Java Goodies Calendars, Clocks, and Calculators sector next brings us to a "DHTML Clock" script, which we'll check over in today's entry. Authored by Paul Swonger in April 1998, the DHTML Clock script creates and displays a running digital clock. You can see the clock at the dhtmlclock.html page; the dhtmlclock.txt Grab the Script page is still available.

As regards running the script, Joe warns: MSIE 4.0 Required - I'll address this in due course, but for the time being, let's just get right to the code, shall we?

A home for our clock

The DHTML Clock clock is housed in a styled, id="Clock" <div>.

<div id="Clock" align="center"
style="font-family:Verdana;font-size:40;color:#0000ff;"> </div>


• The align attribute of the div element is obsoleted by HTML5: use a text-align:center; styling to horizontally center the clock in the div.
• CSS lengths lacking unit identifiers are illegal: specify the font-size:40; declaration as font-size:40px;.

Time starting materials

The clock is set in motion by a tick( ) function that fires when the script document has finished loading.

<script type="text/javascript">
function tick( ) { ... }
window.onload = tick;
</script>


The tick( ) function body begins by declaring eight variables.

var today, intHours, intMinutes, intSeconds, hours, minutes, seconds, ap;

Next, tick( ) creates a new Date( ) object, which is assigned to today; subsequent statements get the today hour, minute, and second, and assign them to intHours, intMinutes, and intSeconds, respectively.

today = new Date( );
intHours = today.getHours( );
intMinutes = today.getMinutes( );
intSeconds = today.getSeconds( );


Display components

The DHTML Clock clock is a 12-hour clock whose display includes an hours part, a minutes part, a seconds part, and an ap a.m./p.m.-type indicator.

hours and ap

With the intHours hour in hand, tick( ) determines the hours and ap parts of the clock via the following if...else if...else if...else series of statements:

if (intHours == 0) { hours = "12:"; ap = "Midnight"; } else if (intHours < 12) { hours = intHours + ":"; ap = "A.M."; } else if (intHours == 12) { hours = "12:"; ap = "Noon"; } else { intHours = intHours - 12; hours = intHours + ":"; ap = "P.M."; }

Somewhat strangely, the ap indicator will read Midnight for the entire 12 a.m. to 1 a.m. hour and Noon for the entire 12 p.m. to 1 p.m. hour - don't know 'bout you, but I don't like that* - here's how I'd handle all of this:

ap = intHours < 12 ? "a.m." : "p.m.";
if (intHours == 0) hours = "12:";
else if (intHours < 13) hours = intHours + ":";
else hours = (intHours - 12) + ":";


• Re the ap assignment, the < operator takes precedence over the conditional operator so there's no need to parenthesize the intHours < 12 condition.

*As it happens, there is in fact a "noonhour" word for the 12 p.m. to 1 p.m. hour, but I'm not gonna use that, either.

minutes and seconds

The minutes and seconds parts of the clock are set with:

if (intMinutes < 10) { minutes = "0" + intMinutes + ":"; }
else { minutes = intMinutes + ":"; }
if (intSeconds < 10) { seconds = "0" + intSeconds + " "; }
else { seconds = intSeconds + " "; }


I would compact these assignments with the conditional operator as well:

minutes = intMinutes < 10 ? "0" + intMinutes + ":" : intMinutes + ":";
seconds = intSeconds < 10 ? "0" + intSeconds + " " : intSeconds + " ";


Going live

The hours, minutes, seconds, and ap strings are concatenated

var timeString = hours + minutes + seconds + ap;

and the resulting timeString string is subsequently loaded into the Clock div.

Clock.innerHTML = timeString;

A setTimeout( ) command updates the clock every 100 milliseconds.

window.setTimeout("tick( );", 100);

The current versions of IE and Netscape in April 1998 were IE 4.x and Netscape 4.x, respectively. Unlike IE 4.x, Netscape 4.x does not support the use of standalone ids as object references - on my computer, Communicator 4.61 throws a Clock is not defined JavaScript Error when it hits the Clock.innerHTML = timeString; line - so that's what the MSIE 4.0 Required business is all about. Microsoft would today recommend that we use the getElementById( ) method to locate the Clock div - see the Note below this example.

document.getElementById("Clock").innerHTML = timeString;

One more point before moving on: Mozilla would today recommend that we use the

window.setTimeout(tick, 100);

setTimeout( ) syntax to update the clock.

A Netscape port

Netscape 4.x doesn't support the innerHTML property, either; indeed, classical JavaScript (JS 1.0-1.3, before the client-side stuff was shipped off to the DOM) doesn't have any properties for setting the text of a textual element - the closest we get is the text property of the anchor/link objects, which is read-only for Netscape 4.x (but is actually writable for some modern browsers, including Firefox). This doesn't mean that we can't adapt the DHTML Clock script for Netscape 4.x, however; here's how:

(1) Give the Clock div a relative positioning so as to layerize it.

body { background-color: white; }
#Clock, #Clock2 { text-align: center; font-family: Verdana; font-size: 40px; color: blue; position: relative; }

<body>
<div id="Clock"></div>


(2-4) Via the document property of the layer object and the write( ) method of the document object, write an id='Clock2' div containing the timeString string to the Clock layer. After that, close( )** the document.Clock.document. Wrap the document.Clock.document commands in an if (document.layers) { ... } conditional to keep non-Netscape 4.x browsers at bay.

if (document.layers) { document.Clock.document.write("<div id='Clock2'>" + timeString + "<\/div>"); document.Clock.document.close( ); } else Clock.innerHTML = timeString;

**It's not necessary to begin with a corresponding open( ) call, but the close( ) operation is definitely necessary to ensure that only one Clock2 div is written in the Clock layer.

Sensibly, Netscape abandoned the layer interface and brought innerHTML on board for Netscape 6.

Old-school dynamicity

The classical way of outputting a JavaScript digital clock is by loading it into an <input type="text">.

document.clockForm.clockInput.value = timeString;

<form name="clockForm" action="">
<input name="clockInput">
</form>


We previously worked with an input digital clock in HTML Goodies' JavaScript Script Tips #25-28, which we discussed in Blog Entry #61.

Does an input clock also count as a DHTML clock? I would certainly say so, given that we are dynamically (the "D" stands for "Dynamic", right?) changing the value attribute of an HTML input element, good enough. Relatedly, I find that Firefox, Google Chrome, and Opera all apply the #Clock styles (vide supra) to an input clock display. BTW, the image-based clock discussed in Blog Entry #359 is a DHTML clock too.

Demo


We may or may not get into some trigonometry in the following entry.

Saturday, March 12, 2016
 
Things of Shapes, Side B
Blog Entry #362

Let's get back now to our discussion of the Areas script's various functions.

areao( ) and volumerp( )

Ovals/ellipses

The terms "oval" and "ellipse" are often used interchangeably, and an "area of an oval" Google search returns many pages for calculating the area of an ellipse; in actuality, an ellipse



has a clearly defined shape and area but an oval doesn't.



With respect to the above ellipse image, the area of an ellipse is given by A = (width / 2) × (height / 2) × π. Assuming that "oval" really means "ellipse" in the Areas script, the areao( ) function gives us something altogether different:

function areao( ) { var length = window.prompt("Enter the length"); var height = window.prompt("Enter the height"); var pi = 3.14159; var sum = (pi * length) / height; window.alert("The area is " + sum + ""); }

I don't know what shape, if any, the pi * length / height calculation is relevant to, but no matter: we'll use the right ellipse area formula in the demo below.

Rectangular prisms

A rectangular prism ("rectangular box" is my preferred term for this shape)



is basically a three-dimensional rectangle; its volume is given by V = length × width × height, and that's what you get with the volumerp( ) function:

function volumerp( ) { var length = window.prompt("Enter length"); var width = window.prompt("Enter width"); var height = window.prompt("Enter height"); var sum = length * width * height; window.alert("The volume is " + sum + ""); }

Image credits

(6) The ellipse image appears courtesy of The GNOME Project. The oval image appears courtesy of ClipartPanda.com.
(7) The rectangular prism image appears courtesy of the 5th Grade Math and Science Team at Southwood Elementary School.
Merci beaucoup, guys.

Demo considerations

I didn't like the prompt( )/alert( ) interface in the Slope script*, and I don't like it in the Areas script, either; once again, I'd much rather gather the user's inputs with <input type="text">s and then display the results on the page by loading them into <samp>s.
(*We covered the Slope script in Blog Entry #360, which provides links to reference pages for the samp element, the getElementsByTagName( ) method, regular expressions, the toFixed( ) method, the Number( ) function, and the isNaN( ) function, which all crop up below.)

I wanted to
(a) access the <input>s for each area/volume determination and
(b) vet the <input>s' values and
(c) bound the number of output digits and
(d) print out the various outputs
via common units of code located in or called by a common getAorV( ) function. I also wanted to
(e) be able to reset the inputs and outputs for each determination
via a common reset( ) function.

I began by putting the HTML for each determination in a separate <div> with a parametric id: triangleDiv, circleDiv, etc. I then gave the aforementioned <samp>s a related set of parametric ids: triangleSamp, circleSamp, etc.

<div id="triangleDiv">
<h3>Area of a triangle</h3>
<img width="218" height="117" src="triangle.gif" alt="A triangle, its base, and its height"><br><br>
A = b × h / 2<br>
<label>Enter the length of the triangle base (b): <input size="5"></label><br>
<label>Enter the height (h) of the triangle: <input size="5"></label><br>
<button type="button" onclick="getAorV('triangle');">Get the area</button><br>
Your area is: <samp id="triangleSamp"></samp><br>
<button type="button" onclick="reset('triangle');">Reset</button>
</div><br> ...


We can now JavaScriptically tie the <div>s and <samp>s together via a general shape variable:

function getAorV(shape) { var shapeInputs = document.getElementById(shape + "Div").getElementsByTagName("input"); ...Vetting code... var area_or_volume; ...Area/volume calculations... if (/\.\d{3,}$/.test(area_or_volume)) area_or_volume = area_or_volume.toFixed(2); document.getElementById(shape + "Samp").textContent = area_or_volume; }

For the Vetting code, I wanted to limit the user inputs to a 0 < x < 100 range, limit the input precision to two places past the decimal point, and of course weed out unwanted strings. Here we go:

for (var i = 0; i < shapeInputs.length; i++) { if (Number(shapeInputs[i].value) <= 0 || 100 <= Number(shapeInputs[i].value) || /\.\d{3,}$/.test(shapeInputs[i].value) || isNaN(shapeInputs[i].value) || shapeInputs[i].value === "") { window.alert("Please enter positive numbers in the range\n0 \u003c x \u003c 100\nand with no more than two post-decimal point digits."); shapeInputs[i].value = ""; shapeInputs[i].focus( ); return; } } /* \u003c is the Unicode escape sequence for the < character. */

The Area/volume calculations themselves can be carried out via a series of if statements or a switch statement, as per your preference.

if (shape == "triangle" || shape == "rhombus") area_or_volume = shapeInputs[0].value * shapeInputs[1].value / 2; if (shape == "circle") area_or_volume = Math.PI * Math.pow(shapeInputs[0].value, 2); // Etc.

And as we know how to access the <input>s and <samp> for each determination it is simple enough to reset them by setting their values/textContent to empty strings:

function reset(shape) { var shapeInputs = document.getElementById(shape + "Div").getElementsByTagName("input"); for (var i = 0; i < shapeInputs.length; i++) shapeInputs[i].value = ""; document.getElementById(shape + "Samp").textContent = ""; }

Demo

The demo below incorporates the code presented in the preceding section - try it out with any inputs you like - the images therein are all 'homemade', BTW.

Area of polygons (and of a circle)!

Area of a triangle



A = b × h / 2



Your area is:


Area of a circle



A = r2 × π


Your area is:


Area of a rectangle



A = w × h



Your area is:


Area of a trapezoid



A = ((b1 + b2) / 2) × h




Your area is:


Area of a rhombus



A = d1 × d2 / 2



Your area is:


Area of an ellipse



A = (width / 2) × (height / 2) × π



Your area is:


Volume of three-dimensional figures

Volume of a rectangular box



V = l × w × h




Your volume is:



Another clock script is next.


Powered by Blogger

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