reptile7's JavaScript blog
Monday, April 30, 2007
 
I'm the Operator with my Pocket Calculator
Blog Entry #74

We've been working with HTML tables in the last couple of scripts, and we'll see another table application today as we tuck into HTML Goodies' JavaScript Script Tips #52, #53, #54, #55, and the script thereof that uses a table to construct a basic calculator (accessible via the "See It In Action" links in all four script tips):



Admittedly, the Script Tips #52-55 calculator is not as "basic" as is my iMac's Calculator desktop accessory:

The iMac Calculator (This is an image, and not an actual calculator.)

However, it's much simpler than my Casio fx-85v.

As for the Script Tips #52-55 Script itself, you can find it here in HTML Goodies' /legacy/beyond/javascript/stips/ subdirectory (the "Here's the Code" links in Script Tips #52-55 all lead to scriptless pages), or you can take it from the div below:

<html>
<head>

<script language="javascript">

<!-- start hiding

function getPi( ) 
{return Math.PI;}

function getRandom( ) 
{return Math.random( );}

function change( )
{
var temp = document.calculator.text.value;

if (temp.substring(0,1) == "-") 
{document.calculator.list.value = "";
document.calculator.text.value = 0 - document.calculator.text.value * 1;}

if (temp.substring(0,1) != "-") 
{document.calculator.list.value ="";
document.calculator.text.value = "-" + temp.substring(0,temp.length);}
}

function recip(x)
{document.calculator.text.value = (1/(x));}

function raisePower(x)
{
var y = 0;

y = prompt("What is the exponent?", "");

document.calculator.text.value = Math.pow(x,y);
}

// end hiding -->
</script>

<!-- Visible Table Starts Here -->

</head>

<body>

<table border="1" cellspacing="2" cellpadding="3%">
<tr>
<form name="calculator">
<td colspan="5" bgcolor="red">
<center>
<input name="list" type="hidden">
<input type="text" name="text" value="">
</td>
<td colspan="2" bgcolor="red">
<center>
<input type="button" value=" Backspace " name="backspace" onclick="document.calculator.text.value = document.calculator.text.value.substring(0,document.calculator.text.value.length*1 -1);">
</td>
<td colspan="2" bgcolor="red">
<center>
<input type="button" value=" Clear " name="clear"
onclick="document.calculator.text.value = '';">
</td>
</tr>
<tr>
<td bgcolor="blue">
<center>
<input type="button" value=" 7 " name="but7" onclick="document.calculator.text.value += '7';">
</td>
<td bgcolor="blue">
<center>
<input type="button" value=" 8 " name="but8" onclick="document.calculator.text.value += '8';">
</td>
<td bgcolor="blue">
<center>
<input type="button" value=" 9 " name="but9" onclick="document.calculator.text.value += '9';">
</td>
<td bgcolor="yellow">
<center>
<input type="button" value=" + " name="add" onclick="document.calculator.text.value += '+';">
</td>
<td bgcolor="yellow">
<center>
<input type="button" value=" - " name="subtract" onclick="document.calculator.text.value += '-';">
</td>
<td bgcolor="lime">
<center>
<input type="button" value=" 1/x " name="reciprocal" onclick="recip(document.calculator.text.value);">
</td>
<td bgcolor="lime">
<center>
<input type="button" value=" x^y " name="power" onclick="raisePower(document.calculator.text.value);">
</td>
<td bgcolor="lime">
<center>
<input type="button" value=" sin " name="sin" onclick="document.calculator.text.value = Math.sin(document.calculator.text.value*3.141592653589793/180);">
</td>
</tr>
<tr>
<td bgcolor="blue">
<center>
<input type="button" value=" 4 " name="but4" onclick="document.calculator.text.value += '4';">
</td>
<td bgcolor="blue">
<center>
<input type="button" value=" 5 " name="but5" onclick="document.calculator.text.value += '5';">
</td>
<td bgcolor="blue">
<center>
<input type="button" value=" 6 " name="but6" onclick="document.calculator.text.value += '6';">
</td>
<td bgcolor="yellow">
<center>
<input type="button" value=" * " name="multiply" onclick="document.calculator.text.value += '*';">
</td>
<td bgcolor="yellow">
<center>
<input type="button" value=" / " name="divide" onclick="document.calculator.text.value += '/';">
</td>
<td bgcolor="lime">
<center>
<input type="button" value=" | x | " name="absolute" onclick="document.calculator.text.value = Math.abs(document.calculator.text.value);">
</td>
<td bgcolor="lime"> 
<center>
<input type="button" value=" exp. " name="exp" onclick="document.calculator.text.value += 'E';">
</td>
<td bgcolor="lime">
<center>
<input type="button" value=" cos " name="cos" onclick="document.calculator.text.value = Math.cos(document.calculator.text.value*3.141592653589793/180);">
</td>
</tr>
<tr>
<td bgcolor="blue">
<center>
<input type="button" value=" 1 " name="but1" onclick="document.calculator.text.value += '1';">
</td>
<td bgcolor="blue">
<center>
<input type="button" value=" 2 " name="but2" onclick="document.calculator.text.value += '2';">
</td>
<td bgcolor="blue">
<center>
<input type="button" value=" 3 " name="but3" onclick="document.calculator.text.value += '3';">
</td>
<td bgcolor="yellow">
<center>
<input type="button" value=" ( " name="leftbracket" onclick="document.calculator.text.value += '(';">
</td>
<td bgcolor="yellow">
<center>
<input type="button" value=" ) " name="rightbracket" onclick="document.calculator.text.value += ')';">
</td>
<td bgcolor="lime">
<center>
<input type="button" value=" x&#178; " name="square" onclick="document.calculator.text.value = document.calculator.text.value * document.calculator.text.value;">
</td>
<td bgcolor="lime">
<center>
<input type="button" value=" round " name="round" onclick="document.calculator.text.value = Math.round(document.calculator.text.value);">
</td>
<td bgcolor="lime">
<center>
<input type="button" value=" tan " name="tan" onclick="document.calculator.text.value = Math.tan(document.calculator.text.value*3.141592653589793/180);">
</td>
</tr>
<tr>
<td bgcolor="blue">
<center>
<input type="button" value=" 0 " name="but0" onclick="document.calculator.text.value += '0';">
</td>
<td bgcolor="blue">
<center>
<input type="button" value=" . " name="decimal" onclick="document.calculator.text.value += '.';">
</td>
<td bgcolor="blue">
<center>
<input type="button" value=" +|- " name="sign" onclick="change( );">
</td>
<td colspan="2" bgcolor="black">
<center>
<input type="button" value=" = " name="equals" onclick="document.calculator.text.value = eval(document.calculator.text.value);">
</td>
<td bgcolor="lime">
<center>
<input type="button" value=" x&#189; " name="sqrt" onclick="document.calculator.text.value = Math.sqrt(document.calculator.text.value);">
</td>
<td bgcolor="lime">
<center>
<input type="button" value=" rand " name="random" onclick="document.calculator.text.value = getRandom( );">
</td>
<td bgcolor="lime">
<center>
<input type="button" value=" pi " name="pi" onclick="document.calculator.text.value += getPi( );">
</td>
</tr>
</table>

</form>

</body>
</html>

First, a few words about that table...

Joe notes in Script Tip #52 that the calculator is housed in a five-row, eight-column table. Actually, a close look at the script suggests that the table should have nine columns, because the colspan attribute values for the td elements holding the input field, the Backspace button, and the Clear button in the first table row are 5, 2, and 2, respectively. However, the Clear button is clearly not meant to span two columns, and the colspan="2" attribute of the Clear button's parent td element can be removed. I should mention, while we're on this topic, that HTML Goodies' "So You Want Some Advanced Table Commands, Huh?" tutorial is largely devoted to the colspan and rowspan attributes, which allow td/th table cells to span more than one column and row, respectively.

Also, let's look at the table element's start-tag for a moment:

<table border="1" cellspacing="2" cellpadding="3%">

A cellpadding value of 3%? This seemed fishy to me initially, but it turns out that both cellspacing and cellpadding are %Length; attributes, and can thus take either a pixel-integer or percentage value, so everything's OK here.

Calculator structure

The calculator comprises a five-column text box

<td colspan="5" bgcolor="red"><input type="text" name="text" value=""></td>

and 33 buttons/keys: 20 of these keys input a number or mathematical operator into the text box, whereas the other 13 keys act on numbers or expressions entered by the input keys.

The input keys

Eighteen of the 20 input keys place a single character in the text box:

(1) The 7, 8, 9, 4, 5, 6, 1, 2, 3, 0, and . keys input the corresponding numbers or a decimal point.

(2) The +, -, *, and / keys input the corresponding arithmetic operators, which we've been using since HTML Goodies' JavaScript Primers #14. If you'd rather put respectively the more conventional × and ÷ symbols on the * and / keys, then that's pretty easy to do:

<!--In place of value=" * ", use:-->
value=" &times; " or value=" &#215; "
<!--In place of value=" / ", use:-->
value=" &divide; " or value=" &#247; "
<!--× and ÷ are not ASCII characters but are part of the Latin-1 Supplement ("upper Latin-1") character set.-->

The - key can also be used to input the - of a negative number.

(3) The ( and ) keys input a left and right parenthesis, respectively. As in mathematics, parentheses can be used in JavaScript to override the precedence of other operators.

(4) The exp. key inputs an E for expressing a number using E notation, e.g., 4E2 is equivalent to 4×102 (i.e., 400); the JavaScript 1.3 Client-Side Guide addresses the use of E notation here.

The other two input keys place multidigit numbers in the text box:

(5) The rand key uses the random( ) method of the core JavaScript Math object, which Joe introduced in the Script Tips #16-18 Script, to input a random number between 0 and 1.

(6) The pi key uses the PI property of the Math object to input pi (3.14159...). If you'd rather put the more conventional π symbol on the pi key, then that's pretty easy to do:

<!--In place of value=" pi ", use:-->
value=" &pi; " or value=" &#960; "

The Script Tips #52-55 Script's script element contains getRandom( ) and getPi( ) functions that respectively generate the rand and pi key inputs. Joe notes in Script Tip #54 that both of these functions are unnecessary; for example, the rand key could be coded as:

<input type="button" value=" rand " name="random" onclick="document.calculator.text.value = Math.random( );">

At the same time, there's something to be said for separating the JavaScript code from the HTML. If we were to follow the calculator form with a script element that (a) contains or (b) references a file that contains

document.calculator.random.onclick = getRandom;
function getRandom( ) {
document.calculator.text.value = Math.random( ); }

then we can code the rand key more cleanly:

<input type="button" value=" rand " name="random" />

On my iMac, the rand and pi key inputs have sixteen significant figures. (In contrast, the RAN# and π functions on my Casio fx-85v give numbers with three and eight significant figures, respectively.) If your browser is up-to-date, then you can pare these numbers down to a more reasonable size with the toPrecision( ) method of the core JavaScript Number object, which we discussed at the end of Blog Entry #30. For example, if you would rather the pi key input 3.14159 and not 3.141592653589793, then the retooled getPi( ) function below will do the job:

function getPi( ) {
var pi6 = (Math.PI).toPrecision(6);
return pi6; }

All of the input keys except the rand key use the += shorthand assignment operator, which Joe introduced in the Script Tips #35-37 Script, to add/concatenate their respective characters to the right-hand end of the text box's value, e.g.:

<input type="button" value=" exp. " name="exp" onclick="document.calculator.text.value += 'E';">

The output keys

(1) The Backspace key uses the substring( ) method of the core JavaScript String object, which we fleshed out in the "SBScroll( ) function" section of Blog Entry #65, to chop off the last character of the text box's value:

<input type="button" value=" Backspace " name="backspace" onclick="document.calculator.text.value = document.calculator.text.value.substring(0,document.calculator.text.value.length*1 -1);">

So, if the user keys in 123456 and clicks the Backspace key, then the 6 is removed and 12345 remains in the text box. Regarding the substring( ) command's second parameter, is it necessary to multiply document.calculator.text.value.length by 1? Emphatically not. Moreover, we can use the this special operator to shorten the onclick expression a bit:

onclick="this.form.text.value = this.form.text.value.substring(0,this.form.text.value.length-1);"

(2) The Clear button

<input type="button" value=" Clear " name="clear" onclick="document.calculator.text.value = '';">

is merely a reset button, and its coding is more complicated than it needs to be;

<input type="reset" value=" Clear " />

is all you need here.

(3) The 1/x key converts a number to its multiplicative inverse (e.g., it would convert 100 to 0.01) via a script element function, recip( ). When clicked, the 1/x key passes to recip( ) document.calculator.text.value, which is assigned to a variable x and then divided into 1:

function recip(x) {
document.calculator.text.value = (1/(x)); }
/*Both sets of parentheses on the right-hand side of the statement above can be removed.*/

Function parameterization is unnecessary, however; we can follow the calculator form with a script element that contains or references

document.calculator.reciprocal.onclick = recip;
function recip( ) {
document.calculator.text.value = 1/document.calculator.text.value; }

and then code the 1/x key as:

<input type="button" value=" 1/x " name="reciprocal" />

In the "Reciprocal" section of Script Tip #53, Joe professes unfamiliarity with the 1/x function, which is a standard feature for all but the most basic calculators. More seriously, in his discussion Joe wrongly links the 1/x key to both the recip( ) and raisePower( ) functions in the script element; in fact, the raisePower( ) function has nothing to do with the 1/x key but pertains to the next key, bringing us to...

(4) The x^y key can be used to exponentiate a number x to the yth power. On a typical calculator, the exponentiation key has a xy label, which cannot be put on an <input type="button"> element but can be put on a <button> element:

<button type="button" name="power" onclick="raisePower(document.calculator.text.value);"> x<sup>y</sup> </button>

(According to its content model

<!ELEMENT BUTTON - - (%flow;)* -(A|%formctrl;|FORM|FIELDSET) -- push button -->

a button element can contain just about any document body element except an anchor element, a form element, a form control element, or a fieldset element - in theory, you could put a table on one of these buttons - the input element, in contrast, has an "EMPTY" content model.)

So, the user keys a number into the text box and clicks the x^y key, triggering the script element's raisePower( ) function and passing thereto document.calculator.text.value, which is again assigned to a variable x. Up pops a prompt( ) box that solicits the user for an exponent; the user's prompt( ) input is outputted/assigned to a variable y. Subsequently, x and y are plugged into a Math.pow( ) command:

function raisePower(x) {
var y = 0; // This declaration can be removed.
y = prompt("What is the exponent?", "");
document.calculator.text.value = Math.pow(x,y); }

(Contra the "Power" section of Script Tip #53, raisePower( ) does not exponentiate x "to the first power" (unless the user enters 1 into the prompt( ) box).)

The caret (^) is an exponentiation operator in some programming languages but in JavaScript is a bitwise XOR operator, whose description is outside the scope of this entry.

(5-7) For the trigonometric sin, cos, and tan keys, the script's author anticipated, not unreasonably, that the user would input an angle in degrees. The sin( ), cos( ), and tan( ) methods of the Math object are designed to work with angles in radians (you can input an angle in degrees but, unless you're inputting 0, you won't get a correct return), which are obtainable from angles in degrees via the following unit conversion:

Degrees-to-radians conversion

The sin key is thus coded as:

<input type="button" value=" sin " name="sin" onclick="document.calculator.text.value = Math.sin(document.calculator.text.value*3.141592653589793/180);">

Regarding the Math.sin( ) parameter, we can of course replace 3.141592653589793 with Math.PI:

onclick="this.form.text.value = Math.sin(this.form.text.value*Math.PI/180);"

The cos and tan keys can be recoded analogously.

(In response to Joe's garbled definition of sine - The ratio of the side opposite the angle of the hypotenuse - I recommend this page at TheMathPage.com if you could use a basic trig refresher.)

(8) The | x | key converts an inputted number to its absolute value via the abs( ) method of the Math object. As a means of converting a negative number to its opposite, the | x | key is redundant, as the +|- key, discussed below, does the same thing.

(9) The key squares an inputted number:

<input type="button" value=" x&#178; " name="square" onclick="document.calculator.text.value = document.calculator.text.value * document.calculator.text.value;">

As shown above, it's not necessary to use the button and sup elements to put a superscripted ² on a button; &#178; (or &sup2;) will do the trick. (The &#178; numeric character reference does not appear in the script on the scripttip52script.html page - I had to fish it out of the source.)

If preferred, a Math.pow( ) command could also be used here:

onclick="this.form.text.value = Math.pow(this.form.text.value,2);"

(10) The round key rounds a number in the text box to the nearest integer via the round( ) method of the Math object, which Joe introduced in the Script Tips #16-18 Script:

<input type="button" value=" round " name="round" onclick="document.calculator.text.value = Math.round(document.calculator.text.value);">

For example, clicking the pi key and then the round key puts 3 in the text box.

(11) The +|- key (labeled +/- on my Casio fx-85v) converts an inputted number to its additive inverse. Joe devotes an entire script tip (#55) to this key, which is associated with the script element's change( ) function and a totally superfluous <input type="hidden"> element. We're not going to discuss the change( ) function, we're just not, because JavaScript has a - unary negation arithmetic operator that allows us to convert positive numbers to their negative counterparts, and vice versa, via a single statement:

<input type="button" value=" +/- " name="sign"
onclick="this.form.text.value = -this.form.text.value;">

That's it - chuck the change( ) function and use the code above.


(12) The key takes the square root of an inputted number via the sqrt( ) method of the Math object:

<input type="button" value=" x&#189; " name="sqrt" onclick="document.calculator.text.value = Math.sqrt(document.calculator.text.value);">

The &#189; numeric character reference does not appear in the script on the scripttip52script.html page, and is again taken from its source. In case you're wondering, 'x&#189;' is the ASCII code to display x1⁄2. Cool, huh? Joe remarks in the "Square Root" section of Script Tip #54. Actually, the &#189; ½ is not an ASCII character but - like ×, ÷, and ² - lies in the upper half of the Latin-1 character set. Interestingly, on my computer the &#189; ½ is not an indivisible character but is rendered as three discrete 1, /, and 2 characters. (So if you were also wondering, "Can't we just type one-slash-two here?", the answer is yes, and IMO, a one-slash-two 1/2 looks a bit nicer than an &#189; ½.)

The ½ should of course be superscripted, which is doable on a button element:

<button type="button" name="sqrt" onclick="this.form.text.value = Math.sqrt(this.form.text.value);"> x<sup>½</sup> </button>

Square roots can alternatively be extracted via a Math.pow( ) command:

onclick="this.form.text.value = Math.pow(this.form.text.value,0.5);"

(As an exercise, you should be able to write a single raisePower( ) function that generates returns for the x^y, , and keys.)

On my Casio fx-85v, the square root key is labeled with a Square root symbol symbol, and we can put a gif image of this symbol on a button element:

<button type="button" name="sqrt" onclick="this.form.text.value = Math.sqrt(this.form.text.value);"><img width="23" height="22" src="image_path/square_root_sign.gif" alt="Square root symbol" /></button>

Output:

Relatedly, an &#8730; or &radic; character reference codes a √ radical symbol, which lacks a vinculum and hence doesn't look as nice.

We'll cover the = key and other aspects of the calculator's operation in the next post.

reptile7

Labels: ,


Comments: Post a Comment

<< Home

Powered by Blogger

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