Replicate iOS Calculator With Plain JavaScript

Category: Javascript | November 9, 2021
Author:erekkles
Views Total:2,427 views
Official Page:Go to website
Last Update:November 9, 2021
License:MIT

Preview:

Replicate iOS Calculator With Plain JavaScript

Description:

An iOS-like web calculator created using plain JavaScript and CSS.

  • AC or C   Clear the last input
  • +/-   Assign the additive inverse
  • %   Find the 1% of the last input
  • ÷   Division operator
  • x   Multiplication operator
  •   Minus operator
  • +   Plus operator
  • =   Equals operator

How to use it:

1. Build the HTML for the iOS calculator.

<div class="calculator">
    <div class="screen">
        0
    </div>
    <div class="keyboard">
        <div class="row">
            <div class="sub-col" data-button-type="operator" data-value="clear" id="clear_button">AC</div>
            <div class="sub-col" data-button-type="operator" data-value="+/-">+/-</div>
            <div class="sub-col" data-button-type="operator" data-value="%">%</div>
            <div class="sub-col" data-button-type="operator" data-value="/">÷</div>
        </div>
        <div class="row">
            <div class="sub-col" data-button-type="digit" data-value="7">7</div>
            <div class="sub-col" data-button-type="digit" data-value="8">8</div>
            <div class="sub-col" data-button-type="digit" data-value="9">9</div>
            <div class="sub-col" data-button-type="operator" data-value="*">x</div>
        </div>
        <div class="row">
            <div class="sub-col" data-button-type="digit" data-value="4">4</div>
            <div class="sub-col" data-button-type="digit" data-value="5">5</div>
            <div class="sub-col" data-button-type="digit" data-value="6">6</div>
            <div class="sub-col" data-button-type="operator" data-value="-">-</div>
        </div>
        <div class="row">
            <div class="sub-col" data-button-type="digit" data-value="1">1</div>
            <div class="sub-col" data-button-type="digit" data-value="2">2</div>
            <div class="sub-col" data-button-type="digit" data-value="3">3</div>
            <div class="sub-col" data-button-type="operator" data-value="+">+</div>
        </div>
        <div class="row">
            <div class="sub-col" data-button-type="digit" data-value="0">0</div>
            <div class="sub-col" data-button-type="digit" data-value=".">,</div>
            <div class="sub-col" data-button-type="operator" data-value="=">=</div>
        </div>
    </div>
</div>

2. The required CSS styles.

.calculator {
  display: flex;
  flex-direction: column;
  min-height: 500px;
  min-width: 290px;
  font-size: 1.3rem;
}
.calculator .screen {
  display: flex;
  justify-content: end;
  align-items: flex-end;
  padding: 0 0 15px 0;
  font-size: 5.5rem;
  width: 290px;
  height: 150px;
  overflow-x: hidden;
}
.calculator .keyboard {
  width: 290px;
  height: 350px;
}
/* first row */
.keyboard .row:first-child .sub-col:not(:last-child) {
  background-color: var(--function-keys);
  color: var(--background-color);
}
.keyboard .row:first-child .sub-col:last-child {
  background-color: var(--operator-keys);
  color: #ffffff;
}
/* second row, third row, fourth row and fifth row */
.keyboard .row:nth-child(2) .sub-col:not(:last-child), 
.keyboard .row:nth-child(3) .sub-col:not(:last-child), 
.keyboard .row:nth-child(4) .sub-col:not(:last-child), 
.keyboard .row:nth-child(5) .sub-col:not(:last-child) {
  background-color: var(--digit-keys);
  color: #ffffff;
}
.keyboard .row:nth-child(2) .sub-col:last-child, 
.keyboard .row:nth-child(3) .sub-col:last-child, 
.keyboard .row:nth-child(4) .sub-col:last-child, 
.keyboard .row:nth-child(5) .sub-col:last-child {
  background-color: var(--operator-keys);
  color: #ffffff;
}
.selected_operation[data-value='clear'], 
.selected_operation[data-value='%'], 
.selected_operation[data-value='+/-'] {
  animation: background-anim-fn .8s ease-out;
}
.selected_operation[data-value='='] {
  animation: background-anim-equals .8s ease-out;
}
/* Select every operator button except clear, %, +/- and = */
.selected_operation[data-button-type='operator']:not(.selected_operation[data-value='clear']):not(.selected_operation[data-value='%']):not(.selected_operation[data-value='+/-']):not(.selected_operation[data-value='=']) {
  background-color: #d4d4d2;
  color: var(--operator-keys);   
  transition: background-color, color .8s ease-out;
}
@keyframes background-anim-fn { 
  0% { background-color: var(--function-keys); }
  50% { background-color: #fff;  }
  100% { background-color: var(--function-keys); }
}
@keyframes background-anim-equals {
  0% { background-color: var(--operator-keys); }
  50% { background-color: #fff;  }
  100% { background-color: var(--operator-keys); }
}
.flex-center {
  justify-content: center;
  align-items: center;
}
.sub-col {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 21%;
  height: 86%;
  -webkit-box-flex: 0;
  -ms-flex: 0 0 21%;
  flex: 0 0 21%;
  max-width: 21%;
  border-radius: 50%;
  transition: background-color .8s ease-out;
}
.row:last-child > .sub-col:first-child {
  display: flex;
  justify-content: start;
  padding-left: 25px;
  width: 47%;
  -ms-flex: 0 0 47%;
  flex: 0 0 47%;
  max-width: 47%;
  border-radius: 100px 100px 100px 100px;
  transition: background-color .8s ease-out;
}
.keyboard .row {
  justify-content: space-between;
  height: 20%;
}
@media only screen and (min-width: 1500px) {
  .calculator {
    min-height: calc(500px / .7);
    min-width: calc(290px / .7);
    font-size: 1.7rem;
  }
  .calculator .screen {
    display: flex;
    justify-content: end;
    align-items: flex-end;
    padding: 0 0 15px 0;
    font-size: 7rem;
    width: calc(290px / .7);
    height: calc(150px / .7);
    overflow-x: hidden;
  }
  .calculator .keyboard {
    width: calc(290px / .7);
    height: calc(350px / .7);
  }
}

3. The main script for the calculator logic.

let first_number = "0";
let second_number = "0";
let result = "0";
let current_operator;
let evaluation = [];
const screen = document.querySelector(".screen");
const keyboard = document.querySelector(".keyboard");
keyboard.addEventListener('click', function(e) {
    e.stopImmediatePropagation()
    onButtonPress(e);
});
function onButtonPress (e) {
    switch(e.target.getAttribute('data-button-type')) {
        case "digit":
            AssignNumber(e)
            break;
        case "operator":
            AssignOperation(e)
            break;
    }
    Render(e);
}
function AssignNumber(e) {
    if(evaluation.length <= 1) {
        first_number = first_number == "0" 
            ? e.target.getAttribute("data-value")
            : first_number + e.target.getAttribute("data-value")
        if(evaluation.length == 1) evaluation.shift();
        evaluation.push(first_number)
        result = first_number;
        return;
    }
    if (evaluation.length >= 2) {
        second_number = second_number == "0"
            ? e.target.getAttribute("data-value")
            : second_number + e.target.getAttribute("data-value");
        if(evaluation.length == 3) evaluation.pop();
        evaluation.push(second_number);
        result = second_number;
    }
}
function AssignOperation(e) {
    current_operator = e.target.getAttribute('data-value');
    // Exclusive operations that can be performed with one number, in the case of clear it can be executed even when the evaluation array is empty
    if(current_operator == "%" || current_operator == "+/-" || current_operator == "clear" || current_operator == "=") return Operate();
    if(evaluation.length == 3) Operate();
    if(evaluation.length == 2) evaluation.pop();
    evaluation.splice(1, 1, current_operator);
}
function Operate() { 
    if(current_operator == "%" && evaluation.length) {
        let number = parseInt(evaluation[evaluation.length - 1])
        result =  (number / 100).toString();
        evaluation.splice(evaluation.length - 1, 1, result);
        return;
    }
    if(current_operator == "+/-" && evaluation.length) {
        result = (evaluation[evaluation.length - 1] * -1).toString();
        evaluation.splice(evaluation.length - 1, 1, result);
        return;
    }
    if(current_operator == "clear") {
        if(evaluation.length <= 2) {
            first_number = "0";
            evaluation = [];
            result = "0";
            return;
        }
        if(evaluation.length == 3) {
            second_number = "0";
            evaluation = [first_number.toString()]
            result = first_number.toString();
            return;
        }
    }
    if(evaluation.length == 3) {
        result = (eval(evaluation.join().replace(/,/g, ""))).toString();
        first_number = result;
        second_number = "0";
        evaluation = [first_number]
    }
}
function Render(e) {
    const clear_button = document.querySelector('div[data-value="clear"]');
    let new_operator_button = e.target;
    let last_operator_button = document.querySelector('.selected_operation');
    last_operator_button ? last_operator_button.classList.remove('selected_operation') : null;
    new_operator_button ? new_operator_button.classList.add('selected_operation') : null;
    // change screen's font-size
    switch(result.toString().length) {
        case 7:
            screen.style.fontSize = "4.7rem"
            break;
        case 8:
            screen.style.fontSize = "4.1rem"
            break;
        case 9: 
            screen.style.fontSize = "3.65rem"
            break
    }
    if(result.toString().length > 9) {
        screen.textContent = parseFloat(result).toPrecision(3);
    } else {
        screen.textContent = result;
    }
    evaluation.length == "0"
        ? clear_button.textContent = 'AC'
        : clear_button.textContent = 'C'
}

You Might Be Interested In:


Leave a Reply