Modern Calculator with HTML5, CSS & JavaScript


TABLE OF CONTENT
Concepts used to make a Modern Calculator
The Process
Exception Handling
Solution Code - Modern Calculator with HTML5, CSS & JavaScript

Let's create a simple Calculator, yet a Modern one to perform some simple algebraic operations using HTML5, CSS and JavaScript.

The calculator looks like this -

How to make a Modern Calculator with HTML5, CSS & Javascript

At a first glance, calculator seems to be a very simple project.

But when we start building it, we realize that the system does not know (or understand simply) what “4”, 7”, ”/”, ”+” actually are.

And it certainly does not know (by itself) how to perform calculations.

The symbols/operands used in the calculator convey the same meaning as in Mathematics. On hovering mouse pointer over them, it highlights the number/operation.

Concepts used to make a Modern Calculator with HTML5, CSS & JavaScript

The prime focus of this calculator is on JavaScript. Constructors and Classes in JavaScript, and Grids in CSS have been used to build this project.

The concepts covered are -

  • How to use ES6 classes to organize code
  • How to sync JavaScript code with a UI
  • CSS Grid
  • Flexbox
  • The best way to cleanly handle user input
  • How to debug complicated edge cases

The Process

First we need to make sure the system assigns proper value to the buttons we created.

Modern calculator - Assigning numbers to keys

Then we make sure it knows which operand performs which operation.

modern calculator - which operands performs which operation

Then we tell the system to initiate the process according to the input from the user.

modern calculator - initiate process based on input

Here we make sure the output that the system has generated gets displayed on the output area.

modern calculator - output displayed

In this calculator user can see two screens, the previous display and the current display, for better convenience.

modern calculator - constructor for dual output
modern calculator - dual display

I have also made The Calculator compatible to Handle Keyboard events i.e. type 1 + 3 to add 1 and 3 and so on.

modern calculator - take keyboard input
modern calculator - take keyboard input code

Exception Handling

There are some error producing conditions I have tried to handle in this project.

  • Some number divided by 0 gives an error.
  • The decimals can go upto 16 places.
  • Suppose we performed a calculation and got a result. We can use the same result as an input number and perform further calculations with it.
  • If a user tries to divide a positive number with a negative number, they will not be able to do so. The negative number which is in the denominator will automatically get converted to positive value.
  • This had been made sure that the user is not able to put two decimals in a number(by mistake).

One of the most important thing while creating a calculator , is handling all the kind of errors that can accidentally generate. The more errors we handle, the more precise our code will be and the more easy to operate the calculator from the user’s end.

Codes for The Modern Calculator

Here are the codes for Modern Calculator with HTML5, CSS & JavaScript.

HTML5 Code -

<!DOCTYPE html>
<html>
<head>
	<title>Calculator</title>
	<link rel="stylesheet" type="text/css" href="calculator.css">
</head>
<body>
	<!-- Here we will be using data attributes for use of components in javascript and -->
	<!-- ids and classes for use in css -->
	<div id="info">Note: While using Keys...Please use <b><i>'X'</i></b> for 
		<b>Multiplication</b>,<b><i>'Enter-key'</i></b> for <b>equal</b>,
		 <b><i>'Esc-key'</i></b> for <b>Clearing screen</b>.
	</div>

	<div id="grid-calculator">
		<div id="display">
			<div id="display-prev" data-prev></div>
			<div id="display-curr" data-curr></div>
		</div>
		<button class="span-two" data-clr>AC</button>
		<button data-del>Del</button>
		<button data-opr>÷</button>
		<button data-num>1</button>
		<button data-num>2</button>
		<button data-num>3</button>
		<button data-opr>*</button>
		<button data-num>4</button>
		<button data-num>5</button>
		<button data-num>6</button>
		<button data-opr>+</button>
		<button data-num>7</button>
		<button data-num>8</button>
		<button data-num>9</button>
		<button data-opr>-</button>
		<button data-num>.</button>
		<button data-num>0</button>
		<button data-eql>=</button>
		<button data-opr>%</button>
	</div>



	<script type="text/javascript" src="calculator.js"></script>
</body>
</html>

CSS Code -

*{
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    font-weight: bold;
    font-size: larger;
}
body{
    background:linear-gradient(to right,rgb(102, 90, 158),rgb(159, 243, 140));
    /* background:linear-gradient(to right,#fa4a3d,rgb(12, 46, 85)); */
}
#grid-calculator{
    /* border:1px solid black; */
    display: grid;
    grid-template-columns: repeat(4,95px);
    grid-template-rows: minmax(30px,auto) repeat(5,80px);
    justify-content: center;
    align-content: center;

    height: 100vh;

}
#display{
    border:1px solid black;
    grid-column: 1/-1;
    background-color: rgba(0, 0, 0, 0.8);
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: flex-end;
    padding: 10px;
    /* important for breaking the large numbers to next line */
    word-wrap: break-word;
    word-break: break-all;
    box-shadow: 0px 0px 2px 1px;
}
#display-prev{
    /* border:1px solid black; */
    min-height: 2rem;
    color: rgba(255, 255, 255,0.75);
    font-size: 1.5rem;
    font-family: monospace;
}
#display-curr{
    /* border:1px solid black; */
    min-height: 3rem;
    color: rgba(255, 255, 255,1);
    font-size: 2.5rem;
    font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
}

.span-two{
    /* border:1px solid black; */
    grid-column: span 2;
}
#grid-calculator > button{
    cursor: pointer;
    outline: none;
    font-size: 2rem;
    border: 1px solid white;
    background: rgba(255, 255, 255, 0.75);
    /* box-shadow: 0px 0px 2px 1px; */
    transition: 0.3s;
}
[data-opr]{
    background: rgba(255, 200, 200, 0.15) !important;
}
[data-clr]{
    background: rgba(25, 255, 255, 0.15) !important;
}
[data-del]{
    background: rgba(255, 20, 255, 0.15) !important;
}
[data-eql]{
    background: rgba(20, 200, 20, 0.25) !important;
}
#grid-calculator > button:hover{
background: rgba(255, 255, 255, 0.3);
}
[data-opr]:hover{
    background: rgba(255, 255, 255, 0.3) !important;
}
[data-clr]:hover{
    background: rgba(255, 255, 255, 0.3) !important;
}
[data-del]:hover{
    background: rgba(255, 255, 255, 0.3) !important;
}
[data-eql]:hover{
    background: rgba(255, 255, 255, 0.3) !important;
}
#info{
    text-align: center;
    font-size: 0.7rem;
}
i{
    font-size: 0.8rem;
}
b{
    font-size: 0.8rem;;
}

JavaScript Code -

const numbers=document.querySelectorAll('[data-num]');
// This creates an array/object of size 11. Look at console.

const operators=document.querySelectorAll('[data-opr]');
// This creates an array/object of size 4. Look at console.

const allClear=document.querySelector('[data-clr]');
const numDelete=document.querySelector('[data-del]');
const equal=document.querySelector('[data-eql]');
const dispPrev=document.querySelector('[data-prev]');
const dispCurr=document.querySelector('[data-curr]');

var i=0;
var j=undefined;
var k=undefined;
var l=undefined;

class Calculator {
    constructor(dispPrev,dispCurr){
        this.dispPrev=dispPrev;
        this.dispCurr=dispCurr;

        // Everytime we create a new Calculator screen will be cleared.
        this.allClear();
    }
    
    // Now we will be creating methods/functions for calculator acc to operations
    // that it will perform.
    
    allClear(){
        // Here we need to clear three things, one is previous screen,current screen
        // and the operator symbol which appears on the previous screen
        
        // Here we are declaring variables this.Prev, this.Curr and this.operation. These are called instance
        // variables. They don't need var, const or let to be declared. We can declare as many
        // as we want. 
        
        this.prev='';
        this.curr='';
        this.operation='';
    }

    delete(){
        // This function will delete the numbers typed in display
        this.curr=this.curr.toString().slice(0,-1);
        // The slice method will remove 0 characters from the left side and
        //  1 character from the right side
    }

    assignNumber(numb){
        // This method is used to add numbers in the current screen
        
        if(this.dispPrev.innerText!=="" && this.curr=="-" && numb!==""){
            this.curr=this.curr.toString().slice(1,-1);
            // return;
        }
        
        if(numb=="." && this.curr.includes(".")){
            return;
        }
        if(this.dispCurr.innerText[0]=="0" && numb!="."){
            this.curr=this.curr.toString().slice(1,0);
        }
        else if(this.dispCurr.innerText[0]=="0" && numb=="."){
            this.curr=this.curr.toString().slice(1,0);
        }
        else if(this.dispCurr.innerText=="=Error" && numb!==""){
            this.curr=this.curr.toString().slice(1,0);
        }
        else if(this.dispCurr.innerText=="=NaN" && numb!==""){
            this.curr=this.curr.toString().slice(3,0);
        }
        else if(this.dispCurr.innerText[0]=="0" && numb!==""){
            this.curr=this.curr.toString().slice(1,0);
        }
        else if(this.dispCurr.innerText[0]=="-" && this.dispCurr.innerText[1]=="0" && numb!=="."){
            this.curr=this.curr.replace("0","");
        }
        this.curr=this.curr.toString()+numb.toString();
        
    }


    chooseOperation(operation){
        // This method is used to perform operations based on operators
        
        if(this.dispCurr.innerText=="" && operation=="-"){
            this.curr="-"
            return;
        }
        if(this.dispPrev.innerText!=="" && this.curr=="-" && operation!==""){
            this.curr=this.curr.toString().slice(1,-1);
            return;
        }
        if(this.dispCurr.innerText=="=Error" && operation!==""){
            this.curr=this.curr.toString().slice(1,0);
            return;
        }
        
        if(this.curr==''){
            return;
        }
        
        if(this.prev!=='' && operation!=="%"){
            this.compute();
        }
        else if(this.prev!=='' && this.dispPrev.innerText.includes("+") && operation==="%"){
            this.operation="/100";
            k=this.dispPrev.innerText;
            l=k.slice(0,-1);
            this.curr=eval(l+""+this.operation+""+"*"+this.curr);
            this.operation='+';
        }
        else if(this.prev!=='' && this.dispPrev.innerText.includes("-") && operation==="%"){
            this.operation="/100";
            k=this.dispPrev.innerText;
            l=k.slice(0,-1);
            this.curr=eval(l+""+this.operation+""+"*"+this.curr);
            this.operation='-';
        }
        else if(this.prev!=='' && this.dispPrev.innerText.includes("*") && operation==="%"){
            this.operation="/100";
            this.curr=eval(this.curr+""+this.operation);
            this.operation='*';
        }
        else if(this.prev!=='' && this.dispPrev.innerText.includes("/") && operation==="%"){
            this.operation="/100";
            this.curr=eval(this.curr+""+this.operation);
            this.operation='/';
        }
        
        if(operation!=="%"){
            this.operation=operation;
            this.prev=this.curr;
            this.curr='';
        }
        else if(operation==="%" && this.prev==''){
            this.operation="/100";
            
            j=this.curr;
            this.curr=eval(j+""+this.operation);
            this.prev='';
            this.operation='';
        }
        
        
        

    }

    compute(){
        // Take the entered values and compute a single value that 
        // we need to print in our Calculator. 
  
        this.curr=eval(this.prev+""+this.operation+""+this.curr);
        if(this.curr=="Infinity"){
            this.curr="Error";
        }
        else if(this.curr=="-Infinity"){
            this.curr="Error";
        }
        else if(this.curr=="NaN"){
            this.curr="Error";
        }
        this.prev='';
        this.operation='';

    }

    updateDisplay(){
        // This is going to update all values on the screen
        if(i>0){
            i=i-1;
            this.dispCurr.innerText="="+this.curr;
        }else{
            this.dispCurr.innerText=this.curr;
        }
        if(this.operation!=null){
        this.opr=this.operation;
        this.dispPrev.innerText=this.prev+this.opr;
        }
    }
}

const calculator=new Calculator(dispPrev,dispCurr);

numbers.forEach(button => {
    button.addEventListener('click', () => {
        calculator.assignNumber(button.innerText);
        calculator.updateDisplay();
    })
})

operators.forEach(button => {
    button.addEventListener('click', () => {
        
        if(button.innerText=="÷"){
            calculator.chooseOperation("/");
        }
        else{
            calculator.chooseOperation(button.innerText);
        }
        
        calculator.updateDisplay();
    })
})


allClear.addEventListener('click', () => {
    calculator.allClear();
    calculator.updateDisplay();
})

equal.addEventListener('click', () => {
    i=i+1;
    calculator.compute();
    calculator.updateDisplay();
})

numDelete.addEventListener('click', () => {
    calculator.delete();
    calculator.updateDisplay();
})

document.addEventListener('keydown',event => {
    console.log('Key Down',event.keyCode);
    if(event.keyCode==49){
        calculator.assignNumber("1");
    }
    else if(event.keyCode==50){
        calculator.assignNumber("2");
    }
    else if(event.keyCode==51){
        calculator.assignNumber("3");
    }
    else if(event.keyCode==52){
        calculator.assignNumber("4");
    }
    else if(event.keyCode==53){
        calculator.assignNumber("5");
    }
    else if(event.keyCode==54){
        calculator.assignNumber("6");
    }
    else if(event.keyCode==55){
        calculator.assignNumber("7");
    }
    else if(event.keyCode==56){
        calculator.assignNumber("8");
    }
    else if(event.keyCode==57){
        calculator.assignNumber("9");
    }
    else if(event.keyCode==48){
        calculator.assignNumber("0");
    }
    else if(event.keyCode==190){
        calculator.assignNumber(".");
    }
    else if(event.keyCode==8){
        calculator.delete();
    }
    else if(event.keyCode==191){
        calculator.chooseOperation("/");
    }
    else if(event.keyCode==88){
        calculator.chooseOperation("*");
    }
    else if(event.keyCode==187){
        calculator.chooseOperation("+");
    }
    else if(event.keyCode==189){
        calculator.chooseOperation("-");;
    }
    else if(event.keyCode==13){
        calculator.compute();
        i=i+1;
    }
    else if(event.keyCode==27){
        calculator.allClear();
    }
    calculator.updateDisplay();
});

Hope you enjoyed making this simple and modern looking calculator with HTML5, CSS & JavaScript.

You may like to read :

3 Super Mini Projects using jQuery

Cheers!

Happy Coding.

About the Author

This article was authored by Rawnak.

Comments are closed.