In this article we'll be building a basic non-scientific calculator using plain old JavaScript and a few lines of CSS. The calculator will be able to perform your standard mathematical operations as well as the ability to clear the buffer.
Here is a full running demo of the result that you will get at the end:
Define a calculator
Let's start with a quick definition. Because we can't build something if we don't know what it is. A calculator is a digital (or non-digital) tool or device that can perform mathematical calculations, such as addition, subtraction, multiplication, etc. The calculator accepts strings of numbers one at a time, with each moving its current decimal place position by one level. These numbers are then delimited by any mathematical operation, such as +,-, x, / and the calculator awaits for a new series of numbers to be entered. A few key features that we need to take into account:
- A calculator requires the numbers 0 - 9
- It requires standard operators (+, -, /, *)
- Needs to allow for multiple numbers to be chained together (1 + 3 + 4 - 2)
- It requires ability to clear the buffer once we are done (CE)
The HTML
Let's start by building the shape and design of the calculator itself. Essentially, a vertical rectangle with the number 0-9 spaced out evenly, with the 0 appearing somewhere at the bottom where it makes sense to fit it.
This won't be a graphical calculator as you can tell. It is a standard calculator with basic mathematical operations. And currently, it doesn't do anything.
<div class="calculator">
<div class="screen buffer" id="screen">0</div>
<div class="screen total" id="screentotal">0</div>
<div class="operators" id="operators"></div>
<div class="numberpad" id="numpad"></div>
</div>
Here is the CSS used for this particular example:
.calculator
{
width: 500px;
margin: 20px auto;
padding: 10px;
border-radius: 10px;
background: black;
color: white;
box-shadow: 2px 2px 9px #aaa;
position:relative;
}
.calculator .key
{
padding: 10px;
font-weight: bold;
text-align: center;
}
.calculator .numpad
{
padding: 10px;
}
.calculator .numpad > div
{
text-align:center;
cursor:pointer;
display:inline-block;
width:33%;
padding: 10px;
}
.calculator .numpad div:last-child
{
width:100%;
}
.calculator .numpad > div:hover
{
background:#333;
color:white;
}
.calculator .operators
{
display:flex;
}
.calculator .operators > div{
display:inline-block;
font-weight: bold;
padding:10px;
cursor:pointer;
flex: 1 1 0;
background:dodgerblue;
color:white;
}
.screen
{
padding:10px;
background:black;
color:white;
text-align:right;
font-size: 18px;
font-weight:bold;
}
.screen.buffer
{
position:absolute;
top:0px;
right:10px;
font-size:11px;
}
.screen.total
{
font-size: 22px;
padding:20px;
}
Visually, that looks like the following:
Variables
Let's define a few variables before we do anything else.
var operators = ['+', '-', 'x', '/', '=', 'cl'];
var buffer = '';
var currentResult = 0;
var currentOperation = '';
operators - This array will help to build up our row of operator buttons
buffer - Keeps the current number in memory
currentResult - Updated after each operation
currentOperation - Currently selected math operation
Creating the keys
The following function will create the calculator number and operator keys. Note that this an incomplete function. The actual 'click' event handlers are discussed further down below.
function createKeys()
{
// create the number pad
for(let i = 0; i < 10; i++)
{
let key = document.createElement('div');
key.innerHTML = i;
key.dataset.value = i;
key.className = 'key';
document.getElementById('numpad').appendChild(key);
}
// create the row of operators
operators.forEach(function(item){
let key = document.createElement('div');
key.innerHTML = item;
key.dataset.operation = item;
key.className = 'key';
document.getElementById('operators').appendChild(key);
});
}
And that function will be called when the page loads.
window.addEventListener('load', createKeys);
Building the buffer
The buffer is essentially the current number that is being built up as a user clicks on the keys. If a user enters the following for example "1", "4", "5", the actual number is 145 but we need to calculate each numbers decimal place first, before we can perform any mathematical calculation on it. And we can keep track of that in the global variable buffer defined above.
Notice that I'm using a string to store the number. Instead of having to worry about number conversions and decimal places, I'm going to be storing the entire sequence as one string and then converting it to an integer at the very end when I am ready to perform an operation.
Click event handling
Below is the updated createKeys() function with the appropriate event handler logic added for each of the numeric keys.
function createKeys()
{
for(let i = 0; i < 10; i++)
{
let key = document.createElement('div');
key.innerHTML = i;
key.dataset.value = i;
key.addEventListener('click', function(e){
// build up entered number
buffer += this.dataset.value;
});
document.getElementById('numpad').appendChild(key);
}
The operator button ("+", "-", "/", etc) clicks are a little more involved. The first thing that we need to do is to check that we are indeed performing one of the mathematical operations and not clearing the screen (CL). If we are clearing the screen, we will need to reset the buffer back to an empty string, set the current total back to 0 and to reset all screen elements and set them back to 0.
If we are not clearing the screen, then the first thing that we need to do is to calculate the result of any numbers currently in the buffer. And the calculate() helper function will take care of that logic.
That function is defined down below. After the calculation, we need to
clear the current buffer to make room for the new number that will be typed next and we set the currentOperation variable to its new value.
operators.forEach(function(item){
let key = document.createElement('div');
key.className = 'key';
key.innerHTML = item;
key.dataset.operation = item;
key.addEventListener('click', function(e){
if (this.dataset.operation == 'cl')
{
buffer = '';
currentResult = 0;
currentOperation = '';
document.getElementById('screen').innerHTML = '0';
document.getElementById('screentotal').innerHTML = '0';
}
else
{
calculate();
currentOperation = this.dataset.operation;
buffer = '';
document.getElementById('screen').innerHTML= '0';
}
});
document.getElementById('operators').appendChild(key);
});
The calculate function can be defined as follows, with further explanation down below.
function calculate()
{
switch(currentOperation) {
case "+":
currentResult += parseInt(buffer);
break;
case "-":
currentResult -= parseInt(buffer);
break;
case "/":
currentResult /= parseInt(buffer);
break;
case "x":
currentResult *= parseInt(buffer);
break;
case "=":
break;
default:
currentResult = parseInt(buffer);
}
document.getElementById('screentotal').innerHTML = currentResult;
}
Based on the current operation that we have selected, we are going to perform the appropriate mathematical calculation. Note the default operation essentially only happens the very first time that we select any operator. Since we only have 1 value in the buffer at this point, our currentResult is essentially the number currently in the buffer.
Once we are done calculating, we update the screentotal element on the page and we are done.
Last words
This is a relatively simple calculator implementation, but covers some interesting topics in terms of data buffering, state management, and really in just how everything, no matter how simple it seems, always has its own set of complexities.
To get the full-source code check out the link over here.