reptile7's JavaScript blog
Wednesday, April 20, 2016
 
Did We Really Count to Five Hundred?
Blog Entry #365

In today's post we'll go through the Calendars, Clocks, and Calculators (CCC) sector's "Count It!" script. Crafted by "Tigger" in March 1998, the Count It! script counts up from 0 to an inputted positive integer via a running display of integers in a text box. The javagoodies.com/countit.txt document holding the Count It! script code is still live as of this writing.

The script demo at the "Tigger's Counting Script" countit.html page works in a sense but not satisfactorily so. If you input, say, 25 in the Count to: field and then click the button, your 25 will instantly appear in the below-the-button field and you won't see any counting at all, that is, the 024 intermediate numbers zoom by much too fast for you to see them. We'll slow things down below.

Control HTML

The user enters the target number into a name="number" text input; the counting takes place in a name="output" text input. A name="form" form contains the number field, a value=" Count! " push button, and the output field. The display is horizontally centered on the page by a center element.

<center>
<form name="form"><br><br><br>
Count to:<input type="text" name="number" length="3">
<input type="button" value=" Count! " onclick="go(document.form.number.value);">
<input type="text" name="output">
</form></center>


Neither the input element nor any other HTML element has a length attribute; as the target number is capped at 500 (vide infra), I suspect Tigger meant maxlength rather than length.
• FWIW, the starting space character and the ending space character of the " Count! " button label are rendered by all of the browsers on my computer except Firefox.

Let's go( )

Clicking the button calls a go( ) function and passes thereto the number field's value, which is given a number identifier.

<script language="JavaScript">
function go(number) { ... }
</script>
</head>


The go( ) function body first tests if number is greater than 500; if the test returns true, then
(a) a "That's pretty high, so I'll set it to 500" alert( ) pops up and
(b-c) number and the number field's value are lowered to 500.

if (number > 500) { window.alert("That's pretty high, so I'll set it to 500."); number = 500; document.form.number.value = "500"; }

number initially has a string data type and is converted to a number for the number > 500 comparison.

This is as much validation as we get from the go( ) function: there are no checks for non-numeric strings, floating-point numbers, or negative numbers.

With number capped at 500, the for statement below increments an n counter from 0 to number and sequentially loads the n values into the output field.

for (n = 0; n <= number; n++) { document.form.output.value = n; window.setTimeout("", 500); }

A setTimeout( ) command whose expression parameter is set to an empty string and whose msec parameter is set to 500 is meant to serve as a delaying tactic that allows us to see discrete ns as the loop runs. It doesn't work in practice, and you wouldn't expect it to work in practice, because...
window.setTimeout( ) does not stall the script. The script continues immediately (not waiting for the timeout to expire). The call simply schedules a future event.
So, we schedule "" to happen in 500 milliseconds and long before we get to that "" n will have reached number and the latter is all we see. Did the delaying work once upon a time when processors were more primitive than what we have today? Maybe, but that was then and this is now.

More generally, we shouldn't be using a loop to count n in the first place: loops are meant to iteratively execute one or more statements as quickly as possible vis-à-vis in a controlled, gradual manner. However, this doesn't mean that we have to start from scratch to sort out the situation; we can achieve the desired effect if we
(1) have the setTimeout( ) command recursively call go( ),
(2) externalize the n initialization, and
(3) deploy the remaining parts of the loop in a corresponding if conditional.

var n = 0, countID; function go(number) { ...number-vetting code... if (n <= number) { document.form.output.value = n; n++; countID = window.setTimeout(function ( ) { go(number) }, 500); } else n = 0; }

Validation

Let's go back to the original script for a moment. If we leave the number field blank and click the button, then our empty string 'input' is converted to 0 for the n <= number loop condition; in this case, the loop runs for one iteration and document.form.output.value = 0 is as high as we go.

All other non-numeric inputs convert to NaN for the n <= number condition. The 0 <= NaN comparison returns false and therefore the loop doesn't run for any iterations (the condition is evaluated a single time and that's all that happens) and no counting occurs. FYI: According to Netscape, NaN is not equal to anything, including NaN.

For a negative number input, the n <= number condition again returns false from the get-go and no counting occurs. (But perhaps you might like to count down to a negative number - we go in reverse here.)

For a floating-point number input, go( ) counts to Math.floor(Number(number)), which is OK, but I'd rather it count to Math.round(Number(number)).

All things considered, here's the number-vetting code I'd use:

if (Number(number) < 0 || 500 < Number(number) || isNaN(number) || number === "") { window.alert("Please enter a positive integer in the range:\n0 \u2264 n \u2264 500"); document.form.number.value = ""; document.form.number.focus( ); return; } if (number.toString( ).indexOf(".") != -1) { number = Math.round(Number(number)); document.form.number.value = number; window.alert("We're going to round your input to: " + Math.round(number)); }

• Upon round( )ing a floating-point number input in a first go( ) iteration it is necessary to toString( ) number for the indexOf( ) test in subsequent go( ) iterations. Alternatively, if we deparameterize go( ) and (re-)type number as a string at the beginning of the go( ) body via

var number;
function go( ) { number = document.form.number.value; ... countID = window.setTimeout(go, 500); ... }

<input type="button" value=" Count! " onclick="go( );">


then the toString( ) conversion is unnecessary.

\u2264 is the Unicode escape sequence for a ≤ character.

Bells and whistles

Stop it

You type 500 in the number field and click the button. When n hits 100 you think, "All right, I get the idea, let's pull the plug on this." For stopping the count in midstream we can add:

function stopCount( ) { window.clearTimeout(countID); }

<input type="button" value="Stop the Count" onclick="stopCount( );">


Start over

We naturally want to add a reset capability to the display. A complete reset requires us to not only blank the number and output fields but also send n back to 0.

If we hold onto the form form, then adding our reset is no more complicated than appending an <input type="reset" value="Reset" onclick="n=0;"> child to the form.

In the demo below, I replace the form and its <center> parent with a <div style="text-align:center;"> container, give the number field an id="numberInput", and exchange the output field for an id="outputSamp" samp placeholder, and I accordingly clear the decks with:

function resetCount( ) { n = 0; document.getElementById("numberInput").value = ""; document.getElementById("outputSamp").textContent = ""; }

Count to: <input id="numberInput" name="number">
The current count is: <samp id="outputSamp"></samp>
<button type="button" onclick="resetCount( );">Reset</button>


Color it

My demo randomly colors the n numbers with code borrowed from the HTML Goodies JavaScript Script Tips #81-83 script; the colors are accentuated by beefing up the n font-size to 48px.

#outputSamp { font-size: 48px; }

var defClrsArray, colorIndex;
defClrsArray = ["red", "purple", "aqua", "green", "blue", "fuchsia", "orange", "brown"];

function go( ) { ... if (n <= number) { colorIndex = Math.floor(Math.random( ) * defClrsArray.length); document.getElementById("outputSamp").style.color = defClrsArray[colorIndex]; document.getElementById("outputSamp").textContent = n; n++; countID = window.setTimeout(go, 500); } ... }

Demo



The current count is:


The financial countdown

On the CCC portal page, Joe comments that the Count It! script is useless - but fun. Hmmm... I think of the downward counts in the "Fast Money" part of Family Feud. If you won $20,000 by giving the right answers to questions during those counts, that wouldn't be so useless, would it? So let's put 20 seconds on the clock, which will start after I read the first question and you click the button below...
The current count is: 00:20
(I trust you can write out the code for this, yes?)
We'll briefly discuss some more geometry-related scripts and then perhaps start work on a calculator script in the following entry.

Comments: Post a Comment

<< Home

Powered by Blogger

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