reptile7's JavaScript blog
Monday, December 31, 2018
 
Keep On Running
Blog Entry #391

Sometimes life gets in the way of blogging, and that's been the case for me for the last several months. I hope to detail my recent trials and tribulations in due course at my other blog but for the time being I want to get back into my Web coding efforts. So picking up where we left off last March - vis-à-vis our tour of the Calculators, Calendars, and Clocks sector of Java/JavaScript Goodies - the next list item to go under the microscope is "Running Calculator", which comes to us courtesy of Brad "Iceman" Mueller.

The Running Calculator calculates a runner's minutes/mile pace from mileage, hours, minutes, and seconds inputs; a functional demo appears at the aforelinked running.html page and the calculator code itself can be accessed here. I myself am not a runner and I have no idea if minutes/mile is a standard running metric or not, but my intuition tells me that Roger Bannister would say "Sounds good to me," so let's go with it, shall we?

Calculator intro

The Running Calculator display begins with a big Running Calculator heading and some introductory metatext.

<body>
<h1>Running Calculator</h1>
<font size="3">In this JavaScript example you will see how fast you've been running those races. Enter total mileage, hours, minutes, seconds, and click compute. Your pace is displayed in the Minutes/Mile (pace) column.
<!-- The font element is not closed in the source as it should have been. -->


<font size="3"> ... </font> stylistically maps onto font-size:medium;, medium being the "initial" (default) value of the CSS font-size property, and
HTML 5 says it's OK for text nodes to be children of the body element (the strict flavor of HTML 4 didn't),
so you don't need to mark up the metatext at all (e.g., as a p element) if you don't want to.

Interface HTML

The business end of the Running Calculator HTML is a form/table
whose first row holds an
Input Mileage, Hours, Minutes, Seconds, and Minutes/Mile (pace)
run of captions and
whose second row holds a matching set of text inputs.
In addition, a push button and a button are placed
in the second row in the running.txt code and
in a separate third row for the running.html demo.
The running.txt form/table code is given below.

<br><br><form method="post">
<table border="4">
<tr>
<td><div align="center">Input Mileage</div></td>
<td><div align="center">Hours</div></td>
<td><div align="center">Minutes</div></td>
<td><div align="center">Seconds</div></td>
<td><div align="center">Minutes/Mile (pace)</div></td>
</tr>
<tr>
<td><input type="text" name="mileage" size="9">
<td><input type="text" name="hour" size="6">
<td><input type="text" name="minute" size="6">
<td><input type="text" name="second" size="6">
<td><input type="text" name="minute_mile" size="9">
<td><input type="button" value="Compute" onclick="compute(this.form);">
<td><input type="reset" value="Reset" onclick="clear(this.form);">
</table>
</form></body>


We won't be submitting the input name/value data to a processing agent, and we don't need a form element to render the input elements as Iceman did back in the late 1990s, so we could lose the outer <form> if we wanted to, but I am inclined to keep it as it allows us to very easily add a reset capability without the need for any JavaScript; that said, if you are determined to put a
for (var i = 0; i < runningInputs.length - 2; i++) runningInputs[i].value = "";
reset in the code, well, I won't stop you.

We don't need the <table> either - we could easily replace each column with a corresponding span or div - but because we are working with a grid of data (a small grid of data, yes, but a grid of data nonetheless), I'm inclined to keep that guy too.

Each caption is horizontally centered in its cell by a <div align="center"> ... </div>; much better to give the first-row <tr> an id="firstTr" and then horizontally center all of the captions in one go via a
#firstTr > td { text-align: center; }
style rule.

With those divs out of the way, let's <label> the captions and explicitly associate them with their corresponding controls:

<td><label for="mileage">Input Mileage</label></td>
<td><label for="hour">Hours</label></td>
...
<td><input type="text" id="mileage" name="mileage" size="9">
<td><input type="text" id="hour" name="hour" size="6">
...


We could fairly convert the first-row <td>s to <th>s per this W3C example but as we've got only one data row I'd say we should leave those <td>s be.

As shown above, the second-row <tr> and <td>s are not closed, which is legit although I myself would pencil in their end tags.

The text inputs' type and name attributes are unnecessary - text is the default type value; we could access the inputs ordinally (e.g., document.forms[0].elements[0]) as opposed to associatively via name or id attributes - but I feel no burning need to throw them out.

The Minutes/Mile (pace) output will be inputted into the minute_mile field by a compute( ) function, which we'll go through in our next episode; the minute_mile.value is not meant to be modified by the user so perhaps we should give minute_mile a readonly attribute.

<!-- Old school: -->
<input type="text" name="minute_mile" size="9" readonly>
<!-- New school: -->
<input type="text" name="minute_mile" size="9" readonly="readonly">


The reset, or not

The reset button is equipped with a call to a clear( ) function

function clear(form) { // Set it all to null
    form.mileage.value = "";
    form.hour.value = "";
    form.minute.value = "";
    form.second.value = "";
    form.minute_mile.value = ""; }


that can be sent packing; <input type="reset" value="Reset"> will do the job.

Interestingly, when I recast the original reset button as a
<button type="button" onclick="clear(this.form);">Reset</button>
push button and tried to execute the clear( ) function, nothing happened (it wouldn't clear the mileageminute_mile inputs, an added window.alert("The clear( ) function has been called."); command did not fire). I ran a for...in probe of the window object

<div id="windowmembersDiv"></div>
<script type="text/javascript">
var memberstring = "";
for (var i in window)
    memberstring += "window." + i + " = " + window[i] + "<br />";
document.getElementById("windowmembersDiv").innerHTML = memberstring;
</script>


to check for a clear member; a
window.clear = function clear(form) { /* clear( ) function body */ }
line duly appeared in the memberstring output. A clear function javascript Google search led me to a relevant "Is 'clear' a reserved word in JavaScript?" Stack Overflow page at which commenter Felix Kling points out that onclick="clear( );" actually triggers the obsolete document.clear( ) method vis-à-vis a global clear( ) function because the document object comes before the window object in the onclick event handler's "scope chain". Fortunately, bypassing document.clear( ) is no more difficult than prepending a window object reference to the onclick clear( ) call, i.e., <button type="button" onclick="window.clear(this.form);">Reset</button>.
We'll discuss the Running Calculator's JavaScript, and perhaps roll out our own demo, in the following entry.

Comments: Post a Comment

<< Home

Powered by Blogger

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