Table of contents
How to build a Calculator using HTML, CSS, and Javascript?
Table of Contents
Introduction
In this article, we will learn about building a Calculator using HTML, CSS, and Javascript. By building the calculator we will learn to use CSS grid and javascript function eval()
. We will build Simple Calculator with some simple operators like +, -, *, and /.
Final Result
Let's start building Calculator
Creating an HTML document
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./style/style.css" />
<title>Calculator using HTML, CSS and Javascript</title>
</head>
<body>
<h1>Calculator</h1>
<div class="calc-container">
<div class="screen"><p></p></div>
<input type="button" value="+" class="keys operators" />
<input type="button" value="-" class="keys operators" />
<input type="button" value="x" class="keys operators" />
<input type="button" value="/" class="keys operators" />
<input type="button" value="7" class="keys" />
<input type="button" value="8" class="keys" />
<input type="button" value="9" class="keys" />
<input type="button" value="=" class="keys equal" />
<input type="button" value="4" class="keys" />
<input type="button" value="5" class="keys" />
<input type="button" value="6" class="keys" />
<input type="button" value="1" class="keys" />
<input type="button" value="2" class="keys" />
<input type="button" value="3" class="keys" />
<input type="button" value="0" class="keys" />
<input type="button" value="." class="keys" />
<input type="button" value="C" class="keys clear" />
</div>
<script src="./script/index.js"></script>
</body>
</html>
- I have added style and script in HTML but both files are empty so the current result looks like this.
Adding CSS
- We will be adding CSS first so that we can work with a more convenient calculator layout
body {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-family: sans-serif;
font-size: 20px;
font-weight: 700;
background-color: rgb(116, 230, 230);
}
h1 {
color: white;
}
.calc-container {
width: 400px;
display: grid;
grid-template-columns: 100px 100px 100px 100px;
grid-template-rows: 100px 70px 70px 70px 70px 70px;
font-family: sans-serif;
font-size: 20px;
font-weight: 700;
overflow: hidden;
border-radius: 10px;
box-shadow: 0px 2px 12px 0px rgb(122, 122, 122);
background-color: black;
}
input {
background-color: white;
border: 1px solid rgb(185, 185, 185);
font-size: inherit;
}
input:hover {
background-color: rgb(237, 237, 238);
}
.operators {
background-color: rgb(223, 222, 222);
}
.screen {
display: flex;
align-items: center;
justify-content: end;
grid-column: 1/5;
height: 100px;
font-size: 35px;
padding: 0px 20px;
color: white;
background-color: rgb(0, 31, 39);
}
.equal {
grid-column: 4/5;
grid-row: 3/7;
background: rgb(226, 110, 74);
}
.equal:hover {
background-color: rgb(224, 90, 49);
}
.clear {
font-weight: 700;
color:rgb(226, 110, 74);
}
.clear:hover {
background-color: rgb(231, 229, 229);
color: black;
}
Now we will add Javascript
- First let's get elements from DOM which will change or from where we get input.
// SELECTING ELEMENTS FROM DOM
const keys = document.querySelectorAll(`.keys`);
let output = document.querySelector(".screen").children[0];
Now we have to listen to all keys for all click events for input.
currently
keys
is an object with a list of nodes having class.keys
we will change that to Array using
Array.from(keys)
. This will return an Array of thiskeys
object.Then we will run
forEach()
loop for this array and for each key we listen to the key and log in to the console to see the output.
Array.from(keys).forEach((keys) => {
keys.addEventListener("click", (event) => {
console.log(event.target.value);
});
});
- We are getting all the events
Now, we have to deal with calculations
- I have used
eval
in-built function. Which will evaluate the string - There are a few cases which we have to handle with if-else statements.
C
- for clearing the screen.x
- that needs to be converted to*
first=
- for evaluating- else we have to add input to the expression string
// VARIABLE FOR CALCULATOR
let expString = "";
let outputStr = "";
// SELECTING ELEMENTS FROM DOM
const keys = document.querySelectorAll(`.keys`);
let output = document.querySelector(".screen").children[0];
// HANDLING KEYS CLICK EVENT USING ARRAY METHOD
Array.from(keys).forEach((key) => {
key.addEventListener("click", (event) => {
if (event.target.value == "C") {
// CLEAR STRING AND SCREEN
expString = "";
outputStr = "";
} else if (event.target.value == "x") {
// HANDLING x CASE
expString = expString + "*";
outputStr = outputStr + "x";
} else if (event.target.value == "=") {
// EVALUATION OF EXPRESSION STRING
try {
expString = eval(expString);
} catch (err) {
// handling errors
expString = "invalid input";
}
outputStr = expString;
} else {
// GENERATING EXPRESSION STRING AND SCREEN OUTPUT
expString = expString + event.target.value;
outputStr = outputStr + event.target.value;
}
// OUTPUT TO THE SCREEN
output.innerHTML = outputStr;
});
});
- After adding this calculator should work fine.
Thing to keep in mind while using eval
We need to validate the string before evaluation.
Why we should validate?
- It is always a good practice to add validation before using
eval()
function. If we don't add validation it can result in security issues. Reason for security issues is that evaluate not only evaluates mathematical expressions but it can also run code or commands.
For more details use this reference: JavaScript eval security best practices
- It is always a good practice to add validation before using
Let's add validation
- For that, we will use regular expression and
test()
function
- For that, we will use regular expression and
- Since this is just a front-end project and we restrict input using predefined values. So, it can't cause any issues but still, we should use that.
const validInputStr = /^[0-9+\-*/. ]*$/;
if(!validInputStr.test(expString)) {
throw new Error("invalid input");
}
Improving User Interface
- If we click equal without any string, it will give
undefined
output, which must be handled. For handling that we will throw an error in the try block so that the catch block will catch error that error.
We will throw that error if we get undefined after evaluation.
// handling undefined case
if (expString === undefined) {
throw new Error("invalid input");
}
- If we click equal with an invalid string like
+
.It will give output asinvalid input
.Then click new input it will be added to thatinvalid input
.
- This is not good for our users we have to handle this. We will do that using a flag. We will clear the string before adding a new character to the string if that flag is
false
. - I have the variable
validInput
for that.
###Final JS Code
// VARIABLE FOR CALCULATOR
let expString = "";
let outputStr = "";
let validInput = true;
// SELECTING ELEMENTS FROM DOM
const keys = document.querySelectorAll(`.keys`);
let output = document.querySelector(".screen").children[0];
// HANDLING KEYS CLICK EVENT USING ARRAY METHOD
Array.from(keys).forEach((key) => {
key.addEventListener("click", (event) => {
// HANDLING INVALID INPUT
if (validInput === false) {
expString = "";
outputStr = "";
validInput = true;
}
if (event.target.value == "C") {
// CLEAR STRING AND SCREEN
expString = "";
outputStr = "";
} else if (event.target.value == "x") {
// HANDLING x CASE
expString = expString + "*";
outputStr = outputStr + "x";
} else if (event.target.value == "=") {
// EVALUATION OF EXPRESSION STRING
try {
const validInputStr = /^[0-9+\-*/. ]*$/;
if (!validInputStr.test(expString)) {
throw new Error("invalid input");
}
expString = eval(expString);
// handling undefined case
if (expString === undefined) {
throw new Error("invalid input");
}
} catch (err) {
// handling errors
expString = "invalid input";
validInput = false;
}
outputStr = expString;
} else {
// GENERATING EXPRESSION STRING AND SCREEN OUTPUT
expString = expString + event.target.value;
outputStr = outputStr + event.target.value;
}
// OUTPUT TO THE SCREEN
output.innerHTML = outputStr;
});
});
Conclusion
It's a great project to learn javascript, eval() function, and handling error. It is also good for learning grid.
There are some suggestions that I want to recommend for your project that you can do in your project. You can also change that in my repository. I will be happy to merge those changes.
- adding the
backspace
key - instead of creating your own error message use the error provided by try block.
If you enjoyed it reading. If any changes you want. Please comment on this blog.