Calculator: Learn How to build a modern Calculator using JavaScript
Tags: javascript, css, frontend, webdev
Published: 2022-03-28 15:13:22

You've probably heard that to get better at anything you need to practice more. That's true because, through deliberate practice, you develop muscle memory. That's where **projects** come in hand. Projects are the best way to learn **JavaScript**, and a calculator is one of the best projects to choose. A calculator covers all the complex interactions with **UI** and **JavaScript**.

In this article I'll walk you through the steps it takes to build a completely modern Calculator using **plain vanilla JavaScript** best practices and **modern ES6** practices

The Calculator will be able to:

- Add, multiply, divide, and subtract operations
  
- Delete operations
  
- Clear All operations
  
- Use decimal numbers
  
- Chain operations
  

**Let's get started**

**Project Setup**: *How to set up your project*

follow the following steps to setup your project:

- Create a new folder named "***project***" and open your code editor (e.g: visual studio code)
  
- create ***index.html***, ***styles.css***, and ***index.js***
  
- link the files inside the HTML
  

### **<u>HTML</u>**

Create a **div** with a class .**calculator** which will wrap everything inside the body tag.

You will also need to create another **div** with a class **.output** with two nested **div** inside it with classes **.previous-operand** and **current-operand** respectively to show the previous operand and the current operand in the Calculator screen
like this:
```html
<div class="calculator">
  <div class = "output">
     <div class="previous-operand"></div>
     <div class="current-operand"></div>
  </div>
</div>
```

Next, create all the buttons on the calculator from "**AC**" to "**=**" button

like this:

```html
<div class="calculator">
   <div class="output">
     <div class="previous-operand"></div>
     <div class="current-operand"></div>
   </div>
   <button class="span-two">AC</button>
   <button>DEL</button>
   <button>÷</button>
   <button>1</button>
   <button>2</button>
   <button>3</button>
   <button>*</button>
   <button>4</button>
   <button>5</button>
   <button>6</button>
   <button>+</button>
   <button>7</button>
   <button>8</button>
   <button>9</button>
   <button>-</button>
   <button>.</button>
   <button>0</button>
   <button class="span-two">=</button>
</div>
```

Ps: Remember, you have to include the class **.span-two** on both "**AC**" and "**=**" buttons to make them span two columns later on.

Note, you may have probably struggled to get the symbol "+". If so, you can google the symbol, copy and paste it.

So far so good.

That's all for the HTML Part, let's jump to **styles.css** and start to style the Calculator to make it look nice.

### **<u>CSS</u>**

Here, you're going to add *CSS* to style the Calculator. First, let's remove the default *browser styles* by doing the following:

```CSS
/** changing default styles of the browser **/
*{
    margin:0;
    padding:0;
    box-sizing:border-box;
}
```

Next, you need to change the style of the body element:

```css
body{
   font-weight:normal;
   font-family:Gotham Rounded, sans-serif;
   background: linear-gradient(to right, rgb(0, 0, 0),rgb(0, 110, 255));
}
```

Next, you need to style the screen by selecting the **.calculator** (wrapping all different buttons and elements) class and style it

like this:

```css
.calculator{
    display:grid;
    align-content:center;
    justify-content:center;
}
```

Oops!! seems like it's not centred vertically!! you can fix that by giving the **.calculator** class the full height which means it's going to fill *100%* of the height all the time:

```CSS
.calculator{
    /** previously written code **/
   min-height: 100vh;
}
```

Alright!! With that fixed, let's move on with styling the **.calculator** class. To make the buttons look like buttons of a calculator, you'll need to use in this case **grid-template-rows** and **grid-template-columns**. so, we are making the *columns* repeating 4 times while giving them 100px wide. *rows* are repeating 5 times while giving them 100px wide and giving them an initial minimum value of 120px tall but maximum of it auto(essentially says; as large as it needs to be to fit everything)

like this:

```css
.calculator{
    display:grid;
    align-content:center;
    justify-content:center;
    min-height:100vh;
    grid-template-rows:minmax(120px, auto) repeat(5,100px);
    grid-template-columns:repeat(4,100px);
}
```

Then, to properly position the buttons, you need to select them and add these styles:

```css
.calculator-grid > button {
    outline: none;
    background-color:rgba(255, 255, 255, .75);
    font-size: 2rem;
    border: 1px solid #fff;
    cursor: pointer;
}
```

Adding the hover effect:

```css
.calculator-grid > button:hover {
       /** previously written code **/
    background-color:rgba(255, 255, 255, .9)
}
```

Now, let's style that **.span-two** class to make the "**AC**" and "**=**" buttons span two columns :

```css
.span-two{
    grid-column: span 2;
}
```

So far so good, but as you can see in your browser, the **.output** section is not properly styled.

To fix that, you need to style the actual **.output** as a whole using this trick :

```css
.output{
    background-color: rgba(0, 0, 0, .75);
    grid-column: 1 / -1;
    display: flex;
    justify-content: space-around;
    align-items: flex-end;
    flex-direction: column;
    padding: 9px;
    word-wrap: break-word;
    word-break: break-all;
}
```

The last thing about CSS is to style the operand which are **.previous-operand** and **.current-operand**. you can do that

like this:

```css
.output .previous-operand {
    font-size : 1.5rem;
    color: rgba(255, 255, 255, .75);
} 
```

```css
.output .current-operand{
    font-size: 2.5rem;
    color: #fff;
}
```

Congrats!! you're done with CSS. Please take a break and come back for the fun part which is JavaScript.

### **<u>JavaScript</u>**

Alright!! let's get started with the most fun part which is coding up the JavaScript. to get started, first you need to select all the different numbers, operands and all the buttons(Clear button, delete button, etc...). To make that easier, you'll need to add **attributes** in **HTML** to be easier to select. this is important because you don't need styling classes with javaScript. the code snippets to do it is like this:

```Html
<!-- Modifying the HTML by adding attributes to be able to select by. -->


<div class="calculator">
   <div class="output">
     <div data-previous-operand class="previous-operand"></div>
     <div data-current-operand class="current-operand"></div>
   </div>
   <button data-all-clear class="span-two">AC</button>
   <button data-delete>DEL</button>
   <button data-operation>÷</button>
   <button data-number>1</button>
   <button data-number>2</button>
   <button data-number>3</button>
   <button data-operation>*</button>
   <button data-number>4</button>
   <button data-number>5</button>
   <button data-number>6</button>
   <button data-operation>+</button>
   <button data-number>7</button>
   <button data-number>8</button>
   <button data-number>9</button>
   <button data-operation>-</button>
   <button data-number>.</button>
   <button data-number>0</button>
   <button data-equals class="span-two">=</button>
</div>
```

Here you go. Since you have added the **attributes** to select the HTML elements, then you can use JavaSript to select them

like this:

```js
const currentOperandElement = document.querySelector('[data-current-operand]');
const previousOperandElement = document.querySelector('[data-previous-operand]');
const numberButtons = document.querySelectorAll('[data-number]');
const operationButtons = document.querySelectorAll('[data-operation]');
const equalsButton = document.querySelector('[data-equals]');
const deleteButton = document.querySelector('[data-delete]');
const allClearButton = document.querySelector('[data-all-clear]');

```

Now that everything is selected, let's see how to use JavaScript to make the calculator works like the normal calculator. The first thing you need to think about is how to store all the information of what's number typed by the user. the easiest way to do that is to use a **Class** just

like this:

```js
class Calculator {
  constructor(previousOperandElement, currentOperandElement) {
    this.previousOperandElement = previousOperand,
    this.currentOperandElement = currentOperand
  }

delete() {

  } 

appendNumber(number) {

  }

clear() {

  }

chooseOperation(operation) {

  }

compute() {

  }

updateDisplay() {

  }

}
```

So, what's going on so far? Above we have created a **Calculator *class*** which holds the **constructor**. This **constructor** will take all the inputs the user will type as well as all the *functions* for our calculator. these functions will be:

- ***delete()***: This function will remove a single number.
  
- ***append number()***: This function will add the number every time the user select that number.
  
- ***Clear()***: This function will clear up all different variables.
  
- ***chooseOperation()***: This function will let the user select which operation to use and make it function effectively.
  
- ***compute()***: This function will take all the values inside the calculator and compute a single value for what we need to display on the screen.
  
- ***updateDisplay()***: This function will update the values inside the output.
  

Next, let's think about the different properties the calculator needs to store. First, you need to know the *current operand* the user is working on, *previousOperand* the user entered, and the operation they selected if any. So you need to be able to remove all these values, and that can be done inside a clear function

like this:

```js
class Calculator {
  constructor(previousOperandElement, currentOperandElement) {
    this.previousOperandElement = previousOperand,
    this.currentOperandElement = currentOperand
    this.clear()
  }

clear() {
    this.previousOperand = "";
    this.currentOperand = "";
    this.operation = undefined;
  } 

}
```

Note: Remember, as soon as we create our ***Calculator***, we need to call **<u>clear()</u>** function as shown above. This is because we need to clear all of the inputs to set them to the default values as soon as we create a new ***Calculator***.

Now that we know all the functions the Calculator will use, let's make all the variables previously created, operate on the Calculator Object like this :

```js
const calculator = new Calculator(previousOperandElement, currentOperandElement);
```

Next, let's make the ***numberButtons*** function

like this:

```js
numberButtons.forEach(button => {
  button.addEventListener('click', () => {
    calculator.appendNumber(button.innerText)
    calculator.updateDisplay()
  })
})
```

Now, you'll need to write the ***appendNumber()*** and ***updateDisplay()*** functions to make them works

like this:

```js
class Calculator {
  /** previously written code **/

    appendNumber(number){
     if (number === "." && this.currentOperand.includes(".")) return
     this.currentOperand = this.currentOperand.toString() + number.toString()
   }
    updateDisplay(){
     this.currentOperandElement.innerText = this.currentOperand;
     this.previousOperandElement.innerText = this.previousOperand;
   }

}
```

Next, let's make the ***operationButtons*** function as well :

```js
operationButtons.forEach(button => {
  button.addEventListener('click', () => {
    calculator.chooseOperation(button.innerText)
    calculator.updateDisplay()
  })
})
```

Now, you'll need to write the ***choose operation*** function to make it work

like this:

```js
class Calculator {

    /** previously written code **/

    chooseOperation(operation) {
      if (this.currentOperand === "") return
      if(this.previousOperand != "") {
        this.compute()
      }
      this.operation = operation;
      this.previousOperand = this.currentOperand;
      this.currentOperand = "";
  }

}
```

Next, let's make the ***equalsButton*** function also :

```js
equalsButton.addEventListener('click', button => {
  calculator.compute()
  calculator.updateDisplay()
})
```

Now, let's work on implementing the ***compute()*** function :

```js
class Calculator {

    /** previously written code **/

    compute() {
    let computation
    const prev = parseFloat(this.previousOperand)
    const current = parseFloat(this.currentOperand)
    if (isNaN(prev) || isNaN(current)) return
    switch (this.operation) {
      case '+':
        computation = prev + current
        break;
      case '-':
        computation = prev - current
        break;
      case '*':
        computation = prev * current
        break;
      case '÷':
        computation = prev / current
        break;
      default:
        return
    }
    this.currentOperand = computation;
    this.operation = undefined;
    this.previousOperand = '';
  }

}
```

Next, let's make the ***allClearButton*** function as well :

```js
allClearButton.addEventListener('click', button => {
  calculator.clear()
  calculator.updateDisplay()
})
```

Next, let's make the ***deleteButton*** function also :

```js
deleteButton.addEventListener('click', button => {
  calculator.delete()
  calculator.updateDisplay()
})
```

Now, let's work on implementing the ***delete()*** function, but here you'll need to use the **slice** method to get the very last value from the **string** and cut it off

like this:

```js
class Calculator {

  /** previously written code **/

    delete() {
    this.currentOperand = this.currentOperand.toString().slice(0, -1);
  }
}
```

Congrats!! Now, the Calculator is entirely functional at a base value but the actual display of our Calculator is not that nice!! we don't have commas between numbers, the operands are not shown up in the previous operand, so it needs to be fixed. Just a couple of lines of code. First thing we need to modify the ***update display()*** function and create ***getDisplayNumber()*** function to delimit numbers with commas

like this:

```js
/** previously written code here **/
updateDisplay() {
    this.currentOperandElement.innerText =
      this.getDisplayNumber(this.currentOperand)
    if (this.operation != null) {
      this.previousOperandElement.innerText =
        `${this.getDisplayNumber(this.previousOperand)} ${this.operation}`
    } else {
      this.previousOperandElement.innerText = ''
    }
  }

/** getDisplayNumber acting like a helper function to delimit numbers with commas **/
getDisplayNumber(number) {
    const stringNumber = number.toString() // for splitting on decimal characters inside it.
    const integerDigits = parseFloat(stringNumber.split('.')[0]) // turning a string to an array.
    const decimalDigits = stringNumber.split('.')[1] // getting second portion out of the array, which is number after decimal place.
    let integerDisplay
    if (isNaN(integerDigits)) {
      integerDisplay = ''
    } else {
      integerDisplay = integerDigits.toLocaleString('en', { maximumFractionDigits: 0 }) // "en" in the localeString means : english.
    }
    if (decimalDigits != null) {
      return `${integerDisplay}.${decimalDigits}`
    } else {
      return integerDisplay
    }
  }
```

Here is what the final version of the project looks like:
![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1647386995023/EiWXywswU.png)
**<u>Conclusion</u>**

Now that you've made it to the end of this project, you have a completely functioning modern JavaScript Calculator working in your browser. if you found this article helpful, please give it a thumbs up. Happy Coding!!