DEV Community

Cover image for Calculator: Learn How to build a modern Calculator using JavaScript
Kebean
Kebean

Posted on • Updated on

Calculator: Learn How to build a modern Calculator using JavaScript

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

HTML

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:

<div class="calculator">
  <div class = "output">
     <div class="previous-operand"></div>
     <div class="current-operand"></div>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

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

like this:

<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>
Enter fullscreen mode Exit fullscreen mode

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.

CSS

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

/** changing default styles of the browser **/
*{
    margin:0;
    padding:0;
    box-sizing:border-box;
}
Enter fullscreen mode Exit fullscreen mode

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

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

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

like this:

.calculator{
    display:grid;
    align-content:center;
    justify-content:center;
}
Enter fullscreen mode Exit fullscreen mode

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:

.calculator{
    /** previously written code **/
   min-height: 100vh;
}
Enter fullscreen mode Exit fullscreen mode

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:

.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);
}
Enter fullscreen mode Exit fullscreen mode

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

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

Adding the hover effect:

.calculator-grid > button:hover {
       /** previously written code **/
    background-color:rgba(255, 255, 255, .9)
}
Enter fullscreen mode Exit fullscreen mode

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

.span-two{
    grid-column: span 2;
}
Enter fullscreen mode Exit fullscreen mode

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 :

.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;
}
Enter fullscreen mode Exit fullscreen mode

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

like this:

.output .previous-operand {
    font-size : 1.5rem;
    color: rgba(255, 255, 255, .75);
} 
Enter fullscreen mode Exit fullscreen mode
.output .current-operand{
    font-size: 2.5rem;
    color: #fff;
}
Enter fullscreen mode Exit fullscreen mode

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

JavaScript

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:

<!-- 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>
Enter fullscreen mode Exit fullscreen mode

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

like this:

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]');

Enter fullscreen mode Exit fullscreen mode

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:

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

delete() {

  } 

appendNumber(number) {

  }

clear() {

  }

chooseOperation(operation) {

  }

compute() {

  }

updateDisplay() {

  }

}
Enter fullscreen mode Exit fullscreen mode

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:

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

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

}
Enter fullscreen mode Exit fullscreen mode

Note: Remember, as soon as we create our Calculator, we need to call clear() 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 :

const calculator = new Calculator(previousOperandElement, currentOperandElement);
Enter fullscreen mode Exit fullscreen mode

Next, let's make the numberButtons function

like this:

numberButtons.forEach(button => {
  button.addEventListener('click', () => {
    calculator.appendNumber(button.innerText)
    calculator.updateDisplay()
  })
})
Enter fullscreen mode Exit fullscreen mode

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

like this:

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;
   }

}
Enter fullscreen mode Exit fullscreen mode

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

operationButtons.forEach(button => {
  button.addEventListener('click', () => {
    calculator.chooseOperation(button.innerText)
    calculator.updateDisplay()
  })
})
Enter fullscreen mode Exit fullscreen mode

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

like this:

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 = "";
  }

}
Enter fullscreen mode Exit fullscreen mode

Next, let's make the equalsButton function also :

equalsButton.addEventListener('click', button => {
  calculator.compute()
  calculator.updateDisplay()
})
Enter fullscreen mode Exit fullscreen mode

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

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 = '';
  }

}
Enter fullscreen mode Exit fullscreen mode

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

allClearButton.addEventListener('click', button => {
  calculator.clear()
  calculator.updateDisplay()
})
Enter fullscreen mode Exit fullscreen mode

Next, let's make the deleteButton function also :

deleteButton.addEventListener('click', button => {
  calculator.delete()
  calculator.updateDisplay()
})
Enter fullscreen mode Exit fullscreen mode

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:

class Calculator {

  /** previously written code **/

    delete() {
    this.currentOperand = this.currentOperand.toString().slice(0, -1);
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

/** 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
    }
  }
Enter fullscreen mode Exit fullscreen mode

Here is what the final version of the project looks like:
image.png
Conclusion

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!!

Discussion (0)