4️⃣

Ch 4. Javascript

Here we do the coding for how the calculator actually functions when we try to do calculations.
 

Calculator Buttons

We start defining the distinct buttons in our calculator by creating variables with constant identifiers. We execute a query using “document query Selector” to return their matching HTML elements.
For example, ‘[data-number]’ (line 1) to select all the number buttons we previously labeled using HTML, and ’[data-operation]’ (line 2) selects our operation buttons. The same applies to the “equal”, “delete”, and “all clear” buttons as well as the previousOperand and currentOperand elements. As the number and operation buttons appear multiple times on our calculator, we must use “document query Selector All” :
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]') const previousOperandTextElement = document.querySelector('[data-previous-operand]') const currentOperandTextElement = document.querySelector('[data-current-operand]')
 
Next, we create a class to save the output of any calculations done as we make our calculator work. We do this by adding a class Calculator onto our button variables. Inside, we have a constructor including all the inputs and functions for our calculator.
We don’t define the previousOperandTextElement and currentOperandTextElement (line 2) so that they can be given values later as we perform operations. The this.clear() function (line 5) is necessary to keep the display empty and reset our inputs:
class Calculator { constructor(previousOperandTextElement, currentOperandTextElement) { this.previousOperandTextElement = previousOperandTextElement this.currentOperandTextElement = currentOperandTextElement this.clear() }
 

Calculator Functions

Now we create functions with separate purposes. These will tell the buttons what to do when we use them. We will use the following functions:
clear() { } delete() { } appendNumber(number) { } chooseOperation(operation) { } compute() { } updateDisplay() { }
 
By creating a new calculator constant and passing everything from our constructor, we can hook up our variables so they will operate on our calculator object. We then input our previous operand and current operand text elements:
const calculator = new Calculator(previousOperandTextElement, currentOperandTextElement)
 
The clear() function removes all values from our display. Removing the output values sets the current and previous operands to empty strings. We then must change this.operation (line 4) to undefined:
clear() { this.currentOperand = ' ' this.previousOperand = ' ' this.operation = undefined }
 
Adding an event listener with button.addEventListener (line 1) lets us invoke a function whenever an event occurs; in this case, clearing all values when the button is clicked. We then call on calculator.updateDisplay (line 4) so our display is updated when we click the button.
allClearButton.addEventListener('click', button => { calculator.clear() calculator.updateDisplay() })
 
The delete() function removes a single value from our display. First we set this.currentOperand = this.currentOperand (line 2). We implement tostring() to turn our variable into a string to obtain the last value using slice(0,-1):
delete() { this.currentOperand = this.currentOperand.toString().slice(0, -1) }
 
We then implement this button with the following code:
deleteButton.addEventListener('click', button => { calculator.delete() calculator.updateDisplay() })
 
The appendNumber() function operates when the user adds a number to the display; it adds the selected numbers on the output screen by updating and appending the “current operand” value. Before performing the operation, we check that our number values that are going to be used in the output contain a period. This makes sure our operation doesn’t execute before we want it to. We use “to string” (line 3) to make sure this.currentOperand is a string that we can easily add another value onto using “+”.
appendNumber(number) { if (number === '.' && this.currentOperand.includes('.')) return this.currentOperand = this.currentOperand.toString() + number.toString() }
 
Next we add the code for executing the function. We use a for.each (line 1) statement on the number button to loop our code over all the number buttons:
numberButtons.forEach(button => { button.addEventListener('click', () => { calculator.appendNumber(button.innerText) calculator.updateDisplay() }) })
 
We use the chooseOperation function to control how our operation buttons function when we click them. First, if the operation is empty (i.e, operation buttons without any numbers), return (line 2) breaks our code. To let our calculator calculate new operations while and complete the previous one simultaneously, we can check that our previous operand isn’t an empty string we call this.compute() (line 3).
 
Upon clicking a number followed by an operation, we want our number to become a previous operand which would allow us to input another number and complete the calculation. For example, in the calculation 7 + 5, the 7 + becomes part of the previous operand in the display and 5 the current. We set this.operation (line 6) to equal the selected operation so our calculator knows which operation to use in our calculation. Setting this.previousOperand = this.currentOperand (line 7) lets us move the current number over to the previous operand once we’re done. The next current operand is cleared by setting it to an empty string (line 8).
chooseOperation(operation) { if (this.currentOperand === ' ') return if (this.previousOperand !== ' ') { this.compute() } this.operation = operation this.previousOperand = this.currentOperand this.currentOperand = ' ' }
 
To work the operation buttons, we can apply the same techniques as we did on the number buttons, only instead of calculator.appendnumber we use chooseOperation(button.innerText) and update the display using calculator.updateDisplay.
operationButtons.forEach(button => { button.addEventListener('click', () => { calculator.chooseOperation(button.innerText) calculator.updateDisplay() }) })
 
The compute() function operates on the values inside our calculator and displays the result. We first create the variable computation (line 2) to store the product of the computation. The following 2 variables are simply the Float versions of our previous and current operands. Next we use (isNaN(prev) || isNaN(current)) (line 5) to check if either of our previous or current variables are not numbers; if they aren’t we use “return” to cancel the code.
We use a switch statement (line 6) to figure out what computation to use for our different operations (addition, subtraction, multiplication, and division). We then assign that value to our computation variable which becomes our current operand:
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 = ' ' }
 
Adding EventListener to the equals button will call the compute function we just wrote. We then need to update the display with our given result:
equalsButton.addEventListener('click', button => { calculator.compute() calculator.updateDisplay() })
 

Calculator Display

We now have a functional calculator; we only need to display our results.
 
The getDisplayNumber() function displays the values on our calculator’s screen. As some very small or large numbers are unable to be converted into floats, we must split them into 2 parts; integer and decimal which can then be turned to floats individually. We set maximum fraction digits to 0 to remove unneeded decimal points.
 
To clear the previous operand, we use an “if-else” statement that checks if this.previousOperandTextElement.innerText is an empty string:
getDisplayNumber(number) { const stringNumber = number.toString() const integerDigits = parseFloat(stringNumber.split('.')[0]) const decimalDigits = stringNumber.split('.')[1] let integerDisplay if (isNaN(integerDigits)) { integerDisplay = ' ' } else { integerDisplay = integerDigits.toLocaleString('en', { maximumFractionDigits: 0 }) } if (decimalDigits != null) { return `${integerDisplay}.${decimalDigits}` } else { return integerDisplay } } }
 
The updateDisplay() function updates the values in the output when we click a button. We check that our operation isn’t null using this.operation != null (line 7) so we can display our previous and current operands. We can call on the helper function getDisplayNumber(number) to add commas to our numbers and show the returned value. The current operand should be added to the updated display whenever selected :
updateDisplay() { this.currentOperandTextElement.innerText = this.getDisplayNumber(this.currentOperand) if (this.operation != null) { this.previousOperandTextElement.innerText = `${this.getDisplayNumber(this.previousOperand)} ${this.operation}` } else { this.previousOperandTextElement.innerText = ' ' } } }
 
Congratulations! You just created a four-function calculator web application!
 
Link to the code:

Next Project

🔫
Tank Game