Previous page
BACK

Part 2a - A calculator
(Simply done!)

 
Contents
  1. Planning
  2. Writing the HTML
  3. Calculator functions
  4. Putting things together

A Simple JavaScript Calculator

Planning

Okay, so we want to write a JavaScript calculator. Let's not (initially) be too ambitious. All our calculator will do is basic arithmetic: add, subtract, multiply and divide. Hmm, what about negative numbers - so we also need to be able to change the sign of a number. What else? Well, we're going to allow rather big numbers, so it's a good idea to have a button that easily allows us to enter an exponent (the number times 10something). We need a decimal point. And we need to be able to clear the display (a CLEAR button), and to clear everything (ALL CLEAR). Let's list out requirements up to now:
  • A display area for numbers
  • Buttons for 0,1,2, up to 9
  • A decimal point button
  • A button to change the sign (+/-)
  • Clear and all clear (C, AC buttons)
  • Function buttons (* / + -)
  • An exponent button (EXP)
  • A 'calculate' button (The = button)

Let's group our different buttons and boxes into three classes:

  1. The display area
  2. The numeric buttons (0 to 9, +/-, .)
  3. Function buttons together with AC, C, = and EXP

Why not position these in a table, thus:

Display Area
Numeric Buttons Function Buttons

Even at this early stage, we also need to think about what can go wrong. Hmm. Here are a few possibilities:

  • Too many digits are entered
  • The result of a calculation is too 'big' to show (too large and positive, or too large and negative)
  • The result of a calculation is too small to show (for example, 1*10-1000)
  • We divide something by zero
  • JavaScript bombs out because of internal calculation problems, for example if we divide 101000 by 101000 - we know that the answer is 1, but does JavaScript?

We therefore must trap numbers that appear too long (although this is to a certain extent a matter of taste); we must show appropriate error messages with divide by zero and numbers that are out of range (most JavaScript implementations seem happy up to about 10±320); tiny tiny tiny numbers can be represented as zero; and we also need a way to alert the user to JavaScript's occasional troubles with large numbers.


Writing the HTML

As our basic calculator, we'll use the following table:

HTML code for our basic calculator
<div align="center">
<table width="30%" border="4">
<tr><td colspan="2" align="center">Display Area</td></tr>
<tr><td>Numeric Buttons</td>
    <td>Function Buttons</td>
</tr></table></div>

Which implements the layout we discussed in "Planning" above. We need to put all of the above inside a <FORM> .. </FORM> pair of tags, and then we need to create buttons for each of the numbers and functions we talked about when we planned things.

How do we create buttons? Well, we could do what we did on the first page of our JavaScript tutorial, and hijack a <input type="reset"> button, but there is a better way. All recent and respectable variants of JavaScript (but not, for example, some earlier releases of IE 3) have the following type of input:

<input type="button">

Which gives us a general-purpose button. To each of these buttons we'll attach a:

OnClick="DoSomething()"

'event handler', so that when we click on the button, we can expect a response. We'll discuss each of these responses in the Calculator Functions section below. For now, let's draw our calculator:

More detailed HTML for basic calculator

<FORM name="Calculator">
 <table border="4">  <!--OUTER BORDER OF CALCULATOR, PANELS-->
  <tr>   <!--TOP PANEL------>
    <td colspan="2">A Simple JavaScript Calculator<br>
      <input type="text" maxlength="40" size="30" name="Display">
    </td>
  </tr>  <!--END TOP PANEL---> 
  <tr>
    <td> <!--LEFT PANEL------>
      <table>
       <tr>
         <td><input type="button" value="7"></td>
         <td><input type="button" value="8"></td>
         <td><input type="button" value="9"></td>
       </tr>
       <tr>
         {all other rows of digits are similar..}
       </tr>
      </table>
    </td><!--END LEFT PANEL-->
    <td> <!--RIGHT PANEL----->
      <table>
         {right panel is similar in overall structure to left}
      </table>
    </td><!--END RIGHT PANEL->
  </tr></table>
</FORM>

We've colour-coded things to make them a bit easier. FORM elements are in red, the outer table is bold, and tables inside tables are in blue. You can see that we've preserved our basic structure that we talked about previously - the top panel takes up two columns and contains a text input that we'll use for displaying results; the bottom left panel will contain the numbers (three examples are given), and the bottom right will contain function buttons. Note several other things:

  • The form has a name. We've called it Calculator.
  • Each input button has a value - this is what will be displayed on the button, for example "7" will be displayed on the button that you push to generate a "7" on the display.
  • The display area is unsurprisingly named Display (with a capital D), and we've defined a width (size="30") and a maximum length of the input string (maxlength="40").
  • Each of the buttons has its own <td> .. </td> - this keeps them nicely spaced out and regular.

Now if you take the above code and simply paste it into your web-page, (adding in a few <td>'s and stuff), you'll get something like the following:

A Simple JavaScript Calculator
{all other rows of digits are similar..}
{right panel is similar in overall structure to left}

.. So clearly we need to do a bit of work. We need to:

  • Pretty up fonts and alignment
  • Widen the buttons a lot
  • Perhaps give the calculator a background colour

If you're adequate at HTML, all of this should be reasonably straightforward. Perhaps widening the buttons needs a bit of explanation. We've given our buttons values of "7", "8" and so on. What happens if we say:

<input type="button" value="      7       " >

This is a good way of widening our buttons. Note that even when we do this, all of the buttons are not quite the same width (at least, on most browsers)! This is because the chances are your browser is using a proportional font to write the "value" on the button. Now in Netscape, the font can be changed for each button to a fixed-width font (although the resulting code looks like hell) - in IE, you don't seem to have that option (I wonder, can you do this using style sheets?). Oh well.

Hmm, just for clarity, why don't we also name our buttons. For example:

<input type="button" value=" 7 " name="seven" >

Play around a bit. Try the 'finished' calculator using both Netscape and IE - you'll find that despite your fine-tuning, the darn thing looks quite different. When you're happy, we're ready to move on to actually making the thing work!

Calculator Functions- Writing the JavaScript

We know how to attach event handlers to each button. We simply put:

OnClick="DoEvent()"

where DoEvent is the event handler for the relevant button. For example, for button number seven, we might say:

<input type="button" name="seven" value=" 7 " OnClick="AddDigit('7')">

where AddDigit is the event handler. Let's now decide what events we need. Here they are:

  • Add a digit to the display
  • Put a decimal point on the display (if not there already)
  • Put an exponent ('e') on the display
  • Change the sign to + if - and minus if plus
  • Clear and All Clear (C and AC)
  • Perform operations * / + and -
  • Calculate (on pressing the = button)
  • Hmm, what if a user types something into the tempting display box. It seems cruel to simply discard this, so we have to enter this (unless it's rubbish)!

Well. Let's think up names for each of these "event handlers" and then implement each of them in turn. While we're about it, how are we going to react when a user types something in the display box? Fortunately, there's an event handler tailored to this need. It's called OnChange. Here are our names for the event handlers:

Name Event What it does
AddDigitOnClickAdd a digit to the display
DotOnClickPut a decimal point on the display (if not there already)
DoExponentOnClickPut an exponent ('e') on the display
PlusMinusOnClickChange the sign to + if - and minus if plus
Clear, AllClearOnClickClear and All Clear (C and AC)
OperateOnClickPerform operations * / + and -
CalculateOnClickCalculate (on pressing the = button)
FixCurrentOnChangeProcess user input

But before we implement these event handlers, we need to sort out a few more things. First we must define the variables we will use in doing our calculations. Here they are:

Variable What it stores
MemoryThe previous number you entered
CurrentThe number currently being entered
OperationThe function button you pushed, for example,
    *    

Finally, we need to define our constants. There is only one:

Constant What it means
MAXLENGTHThe maximum number of digits we'll allow a user to input. Let's make it (say) 30.

Let's get cracking:

1. First, define our constant and global variables

Variables and constant used in our JavaScript
    Memory  = "0";      // initialise memory variable
    Current = "0";      //   and value of Display ("current" value)
    Operation = 0;      // Records code for eg * / etc.
    MAXLENGTH = 30;     // maximum number of digits before decimal!

The only thing that needs explanation in the above is that we used a numeric code for the Operation. Hmm, perhaps this is a bit over the top. We could just keep the "*" or whatever here as a string. But let's stick with this for the time being!

2. Define individual event handlers

2.1 AddDigit - Adding a digit to the display

JavaScript for AddDigit
function AddDigit(dig)          //ADD A DIGIT TO DISPLAY (kept as 'Current')
 {if (Current.length > MAXLENGTH)
       { Current = "Aargh! Too long"; //limit length
       } else
       { if (    (eval(Current) == 0)
              && (Current.indexOf(".") == -1)
            )
           { Current = dig;
           } else
           { Current = Current + dig;
           };
    }; };
   document.Calculator.Display.value = Current;
 }

Here we see how we use the variable Current. If Current has exceeded the allowable length, then we replace it with an error message. Otherwise, we append the digit.

See how we use eval to check to see if Current is zero - if it is, then we replace the zero value with the new digit. This is to prevent a leading zero in numbers - pretty important, as in JavaScript, a leading zero is rather peculiarly used to indicate that we're using OCTAL, that is, base eight.

But wait a bit! What if we are busy entering 0.000 and we press say "7". Unless we check for a decimal, this will replace the 0.000 with 7, which is not what we intended. So we check, using the above code! If Current.indexOf is minus one, this means that there is no decimal point, so we can go on and replace the zero with dig. Otherwise, we simply append the digit to the string in Current.

Then, when everything is over, we store the value in Current into our display area. Note how we do this. We say

document.Calculator.Display.value = Current

(In fact, under Internet Explorer, it's quite okay to leave out the document. portion, as IE works things out for itself. But Netscape will crash and burn if you try this trick). Remember that we called our FORM Calculator, so that's where we got this from, and that we called the <input type="text" > box Display.

The above code can be criticised for several reasons:

  • Perhaps we should first do all the processing, and then check the string length;
  • What if Current is occupied by an error message - should we not test for this? Hmm;
  • We need to handle the case where we are entering the digits for an exponent.

Still, it gives us the flavour of what we should do, doesn't it?

 

2.2 Dot - Putting in a decimal point

JavaScript for Dot
function Dot()                  //PUT IN "." if appropriate.
 {
  if ( Current.length == 0)     //no leading ".", use "0."
    { Current = "0.";
    } else
    {  if ( Current.indexOf(".") == -1)
         { Current = Current + ".";
    };   };
  document.Calculator.Display.value = Current;
 }

This apparently simple function has a touch of redundancy. If the length of Current is zero, ie Current has no contents, then it puts in "0." rather than just the point. In addition, if there is already a "." within Current, it does nothing. Can you see the one remaining problem, not addressed by the above code?

Yep. Once an exponent has been entered, we probably don't want a ".". We should check for this as well, and disable "." if an exponent has been entered!

 

2.3 DoExponent - handling "times 10an integer"

JavaScript for DoExponent
function DoExponent()
 {
  if ( Current.indexOf("e") == -1 )
       { Current = Current + "e0";
         document.Calculator.Display.value = Current;
       };
 }

A simple function. All it does is check if an "e" is already present within Current, and if not, insert the "e" together with a zero exponent (for the time being) just to make things syntactically correct.

 

2.4 PlusMinus - Changing the sign

JavaScript for PlusMinus
function PlusMinus()
 {
  if  (Current.indexOf("e") != -1)  //if there is an exponent:
    { var epos = Current.indexOf("e-");
      if (epos != -1)
         { Current = Current.substring(0,1+epos) + Current.substring(2+epos); //clip -ve exp 
         } else
         { epos = Current.indexOf("e");
           Current = Current.substring(0,1+epos) + "-" + Current.substring(1+epos); //insert
         };
    } else                         //there is NO exponent:
    {  if ( Current.indexOf("-") == 0 )
         { Current = Current.substring(1);
         } else
         { Current = "-" + Current;
         };
    };
  document.Calculator.Display.value = Current;
 }

What does this do? If you use an ordinary calculator and press +/-, see how the sign of the number changes, but (on most machines) if you have entered an exponent, then +/- changes the sign of the exponent!

We'll try and duplicate this. There are two main sections, represented by the bold if .. else. If there is already an exponent, then "e-" is changed to "e", and vice versa.

On the other hand, if there is no exponent, then we check for a leading minus, and clip it out if it is there, or insert it if it isn't.

Can you see the remaining problem? (If there is a zero on the display and you press +/- with the above code, then you'll get "-0", which is a little clumsy). How can you fix this?

 

2.5 Clear and Clear Entry

JavaScript for the Clear Buttons
function Clear()                //CLEAR ENTRY
 { Current = "0";
   document.Calculator.Display.value = Current;
 }

function AllClear()             //Clear ALL entries!
 { Current = "0";
   Operation = 0;                //clear operation
   Memory = "0";                  //clear memory
   document.Calculator.Display.value = Current;
 }

No surprises here. See how Clear simply clears the current number being entered, and AllClear clears everything. We'll explore how Operation works in the following code.

 

2.6 Respond to pressing * / - + buttons

JavaScript to respond to press of arithmetic function
function Operate(op)            //STORE OPERATION e.g. + * / etc.
 {
  if (op.indexOf("*") > -1) { Operation = 1; };       //codes for *
  if (op.indexOf("/") > -1) { Operation = 2; };       // slash (divide)
  if (op.indexOf("+") > -1) { Operation = 3; };       // sum
  if (op.indexOf("-") > -1) { Operation = 4; };       // difference

  Memory = Current;                 //store value
  Current = "";                     //or we could use "0"
  document.Calculator.Display.value = Current;
 }

What do we want to happen when we press say "*" to say "multiply the current number by the number I'm about to enter"? We've chosen to

  1. store the current number in the variable called Memory;
  2. Remember the multiply function in something called Operation; and
  3. clear the display (and Current).

There are lots of other ways we could have done things. See how we've been a bit wicked, in that rather than storing the "*" or "/" or whatever, we've allocated each a numeric code, and we've kept Operation as a number.
( Hmm. Many would frown on this practice as 'not mnemonic' - we've chosen it because we are not entirely happy with JavaScript comparisons in conditional statements, which changed slightly in version 1.2 - perhaps we're just paranoid).

Can you see a problem with the above code? Yes, if we're entering a sequence of operations eg "3" * "4" + "5" then when we press "+" there will be a cock-up! Think about a fix for this when you look at the following code:

 

2.7 Calculate!

JavaScript for Calculating results (On pressing the = button)
function Calculate()            //PERFORM CALCULATION (= button)
 { 
  if (Operation == 1) { Current = eval(Memory) * eval(Current); };
  if (Operation == 2) { Current = eval(Memory) / eval(Current); };
  if (Operation == 3) { Current = eval(Memory) + eval(Current); };
  if (Operation == 4) { Current = eval(Memory) - eval(Current); };
  Operation = 0;                //clear operation
  Memory    = "0";              //clear memory
  document.Calculator.Display.value = Current;
 }

Simple, isn't it? Depending on the operation code, we multiply, divide, add or subtract the number we stored in Memory and the Current number. We use eval to turn the strings in Memory and Current into respectable numbers. Can you see the obvious problems? Here they are:

  • We haven't trapped divide by zero;
  • We aren't aware if a number grows too big or too small;
  • We haven't accounted for the possibility that JavaScript might occasionally return "NaN" (Not a number) when it gets confused!
  • In addition, there's a rather subtle error.

What's the subtle error? Well, if you actually make the calculator (or use our example at the start of this document, which sorts out most of the above problems) you'll see that, after you've performed an operation, you can alter the number (result) that appears on the Display. (This is unlike most conventional calculators!) What is really interesting is that with the above code a JavaScript error results when you press the +/- button immediately after calculating a result! Can you see why?

The answer is in the weak typing of JavaScript. When we say:

Current = eval(Memory) * eval(Current);

we are actually changing Current from a string to a number! Can you think of a way to get it back to a string again?

 

2.8 Dealing with stuff typed into the display area

JavaScript for FixCurrent
function FixCurrent()
 {
  Current = document.Calculator.Display.value;
  Current = "" + parseFloat(Current);
  if (Current.indexOf("NaN") != -1)
    { Current = "Aargh! I don't understand";
    };
  document.Calculator.Display.value = Current;
 }

Finally, we need to handle all the complex things that might be typed by the user in the display area (which we've conveniently left accessible - how would you disable it?).

We use the tricksy built-in JavaScript function parseFloat to see whether the stuff typed in made sense. See how we convert the resulting number to a string by simply adding a null string (The answer to one of my questions above).

Putting it all together

Clearly the above functions are only rough drafts. It is however easy to see the actual code that resulted in our simple calculator. Just click View and then Page Source (or the equivalent) on your browser!

This calculator is by no means perfect. See how many errors you can find! (There are no prizes, but we'd appreciate an email if you find something gross). You can have even more fun (if this is the sort of thing that turns you on) by trying to crash the numerous JavaScript Calculators you'll find on the web.

You can now Continue your lesson

Webpage author jvs@anaesthetist.com Last update: 2000-10-18