In this article, we will discuss how to create a calculator using HTML, CSS, and JavaScript. This project focuses on the most fundamental functionalities, making it an excellent practice project to improve your programming skills as a beginner.
โก๏ธ Get the source code for FREE!
Creating the calculator layout
As usual, let's start by creating the HTML skeleton.
<div class="calculator">
<div class="display">0</div>
<div class="buttons">
<div class="button function">AC</div>
<div class="button function">+/-</div>
<div class="button function">%</div>
<div class="button function">/</div>
<div class="button number">7</div>
<div class="button number">8</div>
<div class="button number">9</div>
<div class="button function">*</div>
<div class="button number">4</div>
<div class="button number">5</div>
<div class="button number">6</div>
<div class="button function">-</div>
<div class="button number">1</div>
<div class="button number">2</div>
<div class="button number">3</div>
<div class="button function">+</div>
<div class="button number">0</div>
<div class="button function">.</div>
<div class="button function">=</div>
</div>
</div>
-
.calculator
is a container that wraps around the whole calculator. -
.display
shows the user input, as well as the output of the calculation. -
.buttons
contains both the.number
buttons (0-9) and the.function
buttons (+
,-
,*
...).
And, of course, you must set up the event listeners so that when a button is clicked, the corresponding function is executed.
<div class="calculator">
<div class="display" id="display">0</div>
<div class="buttons">
<div class="button function" onclick="clearDisplay()">AC</div>
<div class="button function" onclick="toggleSign()">+/-</div>
<div class="button function" onclick="percent()">%</div>
<div class="button function" onclick="appendOperator('/')">/</div>
<div class="button number" onclick="appendNumber('7')">7</div>
<div class="button number" onclick="appendNumber('8')">8</div>
<div class="button number" onclick="appendNumber('9')">9</div>
<div class="button function" onclick="appendOperator('*')">*</div>
<div class="button number" onclick="appendNumber('4')">4</div>
<div class="button number" onclick="appendNumber('5')">5</div>
<div class="button number" onclick="appendNumber('6')">6</div>
<div class="button function" onclick="appendOperator('-')">-</div>
<div class="button number" onclick="appendNumber('1')">1</div>
<div class="button number" onclick="appendNumber('2')">2</div>
<div class="button number" onclick="appendNumber('3')">3</div>
<div class="button function" onclick="appendOperator('+')">+</div>
<div class="button number" id="Num0" onclick="appendNumber('0')">0</div>
<div class="button function" onclick="appendOperator('.')">.</div>
<div class="button function" onclick="calculate()">=</div>
</div>
</div>
Before we start coding, we also need to discuss the general structure of this project.
First of all, there should be a global variable (displayValue
) to store the displayed value, as well as a helper function (updateDisplay()
) that updates the .display
based on the variable. The function selects the display element and updates its content.
There should also be an appendNumber()
and appendOperator()
functions that append new digits to displayValue
. Lastly, you need a calculate()
function that executes the mathematical expression and returns the value.
And of course, there are other functions such as clearDisplay()
, toggleSign()
, and percent()
.
Adding styles
Next, you should add some styles so that our calculator actually looks like a calculator.
First of all, the .buttons
should be a grid layout with 4 columns and 5 rows. As an example, I'm making each cell to be 100px
wide, and with a 5px
gap in between.
.buttons {
display: grid;
grid-template-columns: 100px 100px 100px 100px;
gap: 5px;
}
And then customize the individual buttons to make them look more pleasing.
.button {
padding: 20px 0px;
text-align: center;
}
.function {
background-color: coral;
}
.number {
background-color: bisque;
}
The number 0 is a special case, as it spans over multiple columns. This can be achieved using the grid-column-start
and grid-column-end
properties.
<div class="button number" id="Num0" onclick="appendNumber('0')">0</div>
#Num0 {
grid-column-start: 1;
grid-column-end: 3;
}
As for the display, it may be designed however you like, as long as it looks good to you. Here is what I did:
.calculator {
width: fit-content;
}
.display {
border: 1px solid;
margin-bottom: 5px;
padding: 15px 10px;
text-align: right;
font-family: "Courier New", Courier, monospace;
font-size: xx-large;
}
Adding scripts
And finally, for the most important part, the scripts. First of all, as we've discussed before, you need a global variable to store the value that will be displayed, as well as a helper function that updates the display based on the variable.
let displayValue = "";
function updateDisplay() {
if (displayValue === "") {
document.getElementById("display").innerHTML = "0";
} else {
document.getElementById("display").innerHTML = displayValue;
}
}
Here, I'm using the ID selector getElementById("display")
, so make sure your display element has the correct ID as well.
<div class="calculator">
<div class="display" id="display">0</div>
<div class="buttons">. . .</div>
</div>
innerHTML
points to the content of the selected HTML node.
In this example, if the displayValue
is empty, 0 will be displayed. If not, the actual value will be displayed.
appendNumber()
and appendOperator()
Next, you need an appendNumber()
function that updates the displayValue
by appending new numbers and also updates the display afterward.
function appendNumber(number) {
displayValue += number;
updateDisplay();
}
And of course, the appendOperator()
function does the same thing, but with the operator keys.
function appendOperator(operator) {
if (
displayValue !== "" &&
!isNaN(displayValue.charAt(displayValue.length - 1))
) {
displayValue += operator;
updateDisplay();
}
}
However, this function is a bit tricker for two reasons. First, you can't have two consecutive operators. For example, the equation 12 */--+ 21
doesn't make any sense. So before adding a new operator digit, you must check that the last digit of displayValue
is not an operator.
In this example, displayValue.charAt(displayValue.length - 1)
retrieves the last digit of displayValue
, isNaN()
returns true
if the given value is NOT a number, and !
flips the given value. Putting them together, line 4 returns true
if the last digit of displayValue
is a number.
And second, an equation should not start with an operator, so you have to ensure displayValue
is not empty before adding an operator (displayValue !== ""
).
calculate()
function calculate() {
if (
displayValue !== "" &&
!isNaN(displayValue.charAt(displayValue.length - 1))
) {
try {
const result = eval(displayValue);
displayValue = result.toString();
updateDisplay();
} catch (error) {
displayValue = "Error";
updateDisplay();
}
}
}
The calculate()
function is at the heart of this project, but it is actually rather easy to understand. We are using the built-in function eval()
to do all the heavy lifting. You simply pass the displayValue
to the function eval()
, and it will return the result. However, you should remember to catch the potential errors, just in case the equation doesn't evaluate to a result that makes sense.
In practice, it is not recommended to use
eval()
in the production environment, as it evaluates all JavaScript expressions given to it without proper validation, and this opens the door to code injection attacks. The source code for this article implemented a more production-ready solution, and it is available for free here.
clearDisplay()
function clearDisplay() {
displayValue = "";
updateDisplay();
}
Lastly, the clearDisplay()
function resets displayValue
to an empty string.
Conclusion
You may have noticed we neglected two functions, toggleSign()
and percent()
. The exact implementation of these functions is available in the source code, which you can download for free. But if you prefer implementing your own version, here is a hint:
- The
toggleSign()
function should examine the first digit ofdisplayValue
. If it is a number, add a-
sign in the front. If it is not a number, that meansdisplayValue
is already a negative value, so simply remove it. - The
percent()
function should first convertdisplayValue
into a number, and then calculatedisplayValue / 100
.
If you need more assistance regarding HTML and CSS, check out my course HTML & CSS: A Practical Guide.
Happy coding!
Top comments (2)
Thank you for this impressive demonstation of the shortcomings of the traditional concept of HTML/CSS/JS. See this post for a more "generative" approach using only some lines of Javascript.
Try the code online here
Amazing