Andrew Dinsmore
Andrew Dinsmore

Reputation: 37

Javascript Calculator, how to save display value after equals

This calculator works fine as long as you reset it after each two operand calculation. And all I need it to do, is if I hit equals, the current display value to be used as the first operand in the next calculation. I was proud of my little solution lol, until I tested it. here is my code

var backspaceBtn = document.getElementById("backspace");
var equalsBtn = document.getElementById("equals");
var decimalBtn = document.getElementById("decimal");
var clearBtn = document.getElementById("clear");
var operatorBtns = document.querySelectorAll(".operatorBtn");
var displayValEl = document.getElementById('display');
var calcNumBtns = document.getElementsByClassName("numBtn");
//variables to store operands and operators
var displayVal = "0";
var finalVal = "";
var operator = "";
var num1;
var num2;
// update display when user clicks number
var updateDisplay = (clkObj) => {
  var btnText = clkObj.target.innerText;
  if (displayVal === "0")
    displayVal = "";

  displayVal += btnText;
  displayValEl.innerText = displayVal;
}
//add event listeners to each num button,setting the click functon to update screen
for (let i = 0; i < calcNumBtns.length; i++) {
  calcNumBtns[i].addEventListener("click", updateDisplay, false);
}
// capturing the first number and operator when the operator btns are clicked
var operator1 = (clkObj) => {
  var sign = clkObj.target.innerText;
  switch (sign) {
    case "+":
      operator = "+"
      num1 = parseFloat(displayVal)
      displayVal = "0";
      displayValEl.innerText = displayVal
      break;
    case "-":
      operator = "-"
      num1 = parseFloat(displayVal)
      displayVal = "0";
      displayValEl.innerText = displayVal
      break;
    case "÷":
      operator = "÷"
      num1 = parseFloat(displayVal)
      displayVal = "0";
      displayValEl.innerText = displayVal
      break;
    case "x":
      operator = "x"
      num1 = parseFloat(displayVal)
      displayVal = "0";
      displayValEl.innerText = displayVal
      break;
    default:
      break;
  };
}
//adding event listener to the operator btns, setting click to update num1 and operator variables
for (let i = 0; i < operatorBtns.length; i++) {
  operatorBtns[i].addEventListener("click", operator1, false);
}
//adding decimal functionality, checking to see if one is present already. 
decimalBtn.onclick = () => {
  if (!displayVal.includes("."))
    displayVal += "."
  displayValEl.innerText = displayVal;
}
//capturing the second value and performing the operation
equalsBtn.onclick = () => {
  num2 = parseFloat(displayVal);
  switch (operator) {
    case '+':
      finalVal = num1 + num2;
      displayValEl.innerText = finalVal;
      break;
    case '-':
      finalVal = num1 - num2;
      displayValEl.innerText = finalVal;
      break;
    case 'x':
      finalVal = num1 * num2;
      displayValEl.innerText = finalVal;
      break;
      //nesting switches to check for division by zero.
    case '÷':
      switch (true) {
        case num1 === 0 || num2 === 0:
          alert("Cant devide by Zero")
          break;
      }
      finalVal = num1 / num2;
      displayValEl.innerText = finalVal;
      break;
    default:
      break;
  }
  num1 = displayVal;
  num2 = 0;
  operator = "";
}

clearBtn.onclick = () => {
  displayVal = "0";
  operator = "";
  num1 = 0
  num2 = 0
  displayValEl.innerText = displayVal;
}

backspaceBtn.onclick = () => {
  let lengthOfDisplay = displayVal.length;
  displayVal = displayVal.slice(0, lengthOfDisplay - 1)

  if (displayVal === "")
    displayVal = "0";

  displayValEl.innerText = displayVal;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" type="text/css" href="style.css">
  <title>Document</title>
</head>

<body>
  <header id="h-div">
    <p class="header-text">JavaScript Calculator</p>
  </header>
  <div class="background-wrapper">
    <div id="wrapper" class="main-grid">
      <div id="display">0</div>
      <div id="calc-body" class="calc-grid">
        <div id="add" class="calc-button operatorBtn btnText">+</div>
        <div id="subtract" class="calc-button operatorBtn btnText">-
        </div>
        <div id="multiply" class="calc-button operatorBtn 
btnText">x</div>
        <div id="divide" class="calc-button operatorBtn btnText">÷</div>

        <div id="num7" class="calc-button numBtn btnText">7</div>
        <div id="num8" class="calc-button numBtn btnText">8</div>
        <div id="num9" class="calc-button numBtn btnText">9</div>
        <div id="backspace" class="calc-button btnText 
backspaceBtn">&#8676;</div>

        <div id="num4" class="calc-button numBtn btnText">4</div>
        <div id="num5" class="calc-button numBtn btnText">5</div>
        <div id="num6" class="calc-button numBtn btnText">6</div>
        <div id="clear" class="calc-button btnText clearBtn">C</p>
        </div>

        <div id="num1" class="calc-button numBtn btnText">1</div>
        <div id="num2" class="calc-button numBtn btnText">2</div>
        <div id="num3" class="calc-button numBtn btnText">3</div>
        <div id="decimal" class="calc-button btnText decimalBtn">.</div>

        <div id="num0" class="calc-button numBtn btnText">0</div>
        <div id="equals" class="calc-button btnText operator-Btn 
 equalsBtn">=</div>
      </div>
    </div>
  </div>
  <script src="calcApp.js"></script>
</body>

</html>

I've played around with it, but cant seem to get what i need to do without a total re-write.If you enter a number, then press an operator and add another number, then press equals, you get the correct answer. even decimals. But if you leave the answer on screen, and just try to press an operator and a second operand, then equals again, its very badly wrong. Ive added in the HTML

Upvotes: 1

Views: 3491

Answers (2)

Teiem
Teiem

Reputation: 1629

Here is a different approach to your problem - I add the text of the button I pressed to a string and evaluate the string if needed:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="numbers">
        <div class="number">0</div>
        <div class="number">1</div>
        <div class="number">2</div>
        <div class="number">3</div>
        <div class="number">4</div>
        <div class="number">5</div>
        <div class="number">6</div>
        <div class="number">7</div>
        <div class="number">8</div>
        <div class="number">9</div>
    </div>

    <div id="operations">
        <div class="operation">+</div>
        <div class="operation">-</div>
        <div class="operation">*</div>
        <div class="operation">/</div>
    </div>

    <div id="control">
        <div id="equal">=</div>
        <div id="reset">C</div>
    </div>

    <div id="result">Res</div>


</body>

</html>

<style>
    body {
        width: 100%;
        height: 100%;
        margin: 0;
        background-color: #96a5af;
    }


    #numbers>.number {
        display: inline;
    }

    #operations>.operation {
        display: inline;
    }

    #contro>* {}
</style>


<script>
    // getting all nodes
    const numbers = Array.from(document.getElementById("numbers").children);
    const operations = Array.from(document.getElementById("operations").children);

    const calc = document.getElementById("equal");
    const equal = document.getElementById("result");
    const reset = document.getElementById("reset");

    // calcualtion is the calculation we watnt to do
    let calculation = "";

    // [...numbers, ...operations] combines the numbers and operations array so I dont have to create an eventlister for both
    [...numbers, ...operations].forEach((el) => {
        document.addEventListener("click", (e) => {
            //My Code is not Ideal, so I check here wearhter I accually clicked on the button I wanted to
            if (e.target.isSameNode(el)) {
                // and add the text of the button to my calculation
                calculation += e.target.innerText;
                console.log('e.target.innerText', e.target.innerText)

                // if I just added a number, I show my current result
                if (!isNaN(parseFloat(+e.target.innerText)) && isFinite(e.target.innerText)) {
                    evaluate();
                }
            }

        })
    })

    // manually calculate result
    calc.addEventListener("click", () => {
        evaluate()
    })

    // reset by clearing calculation
    reset.addEventListener("click", () => {
        calculation = "";
    })

    // helper function which calculates result and sets my HTML to it
    const evaluate = () => {
        equal.innerHTML = eval(calculation) + "";
    };
</script>

codepen: https://codepen.io/anon/pen/qgpove

Edit: An Example on how to do the calculation without eval

const str = "3+45*5/5+3";

const evalute = () => {
    let strArr = str.split(/([+\-*/](?=[0-9]+))|(?=[+\-*/])/);
    let pos;

    do  {
        pos = strArr.findIndex(el => el == "*")
        if (!!~pos) strArr.splice(pos - 1, 3, +strArr[pos - 1] * +strArr[pos + 1]);
    } while (!!~pos);

    do  {
        pos = strArr.findIndex(el => el == "/")
        if (!!~pos) strArr.splice(pos - 1, 3, +strArr[pos - 1] / +strArr[pos + 1]);
    } while (!!~pos);

    do  {
        pos = strArr.findIndex(el => el == "+")
        if (!!~pos) strArr.splice(pos - 1, 3, +strArr[pos - 1] + +strArr[pos + 1]);
    } while (!!~pos);

    do  {
        pos = strArr.findIndex(el => el == "-")
        if (!!~pos) strArr.splice(pos - 1, 3, +strArr[pos - 1] - +strArr[pos + 1]);
    } while (!!~pos);

    return strArr[0];
}

console.log(evalute(str));

Upvotes: 1

Brett Caswell
Brett Caswell

Reputation: 1504

you want to persist the changes and utilize the persisted value in subsequent calculations. So, you're essentially going to refactor your equalsBtn click handler to do that.

equalsBtn.onclick = () => {  
  num2 = parseFloat(displayVal);

  switch (operator) {
    case '+':
      finalVal += num2;          
      break;
    case '-':
      finalVal -= num2;
      break;
    case 'x':
      finalVal *= num2;  
      break;
    case '÷':
      //for division by zero.
      if (num2 === 0)
      {
        alert("Cant divide by Zero");
        return;
      }
      finalVal /= num2;
      break;
    default:
      break;
  }
  displayValEl.innerText = finalVal.toString();
  num1 = num2;
  num2 = 0;
  operator = "";
};

You'll also need to ensure finalVal gets cleared in the clearBtn click handler.

clearBtn.onclick = () => {
  displayVal = "0";
  operator = "";
  num1 = 0;
  num2 = 0;
  finalVal = 0;
  displayValEl.innerText = displayVal;
};

EDIT: In Addition, there are some scenarios you should handle regarding state of this value persistence: in the scope of initializing, updateDisplay, operator click, and deleteBtn click to address other expectations.

var backspaceBtn = document.getElementById("backspace");
var equalsBtn = document.getElementById("equals");
var decimalBtn = document.getElementById("decimal");
var clearBtn = document.getElementById("clear");
var operatorBtns = document.querySelectorAll(".operatorBtn");
var displayValEl = document.getElementById('display');
var calcNumBtns = document.getElementsByClassName("numBtn");
//variables to store operands and operators
var displayVal = "0";
var operator = "";
var finalVal = 0;
var num1 = 0;
var num2 = 0;

// update display when user clicks number
var updateDisplay = (clkObj) => {
  var btnText = clkObj.target.innerText;

  var shouldResetCalc = (operator === "" && finalVal !== 0);
  
  displayVal = (displayVal === "0" || shouldResetCalc)
    ? btnText 
    : displayVal + btnText;

  if (shouldResetCalc)
    finalVal = 0;
    
  displayValEl.innerText = displayVal;
}

//add event listeners to each num button,setting the click functon to update screen
for (let i = 0, max = calcNumBtns.length; i < max; i++) {
  calcNumBtns[i].addEventListener("click", updateDisplay, false);
}

// capturing the first number and operator when the operator btns are clicked
var operator1 = (clkObj) => {
  var sign = clkObj.target.innerText;

  switch (sign) {
    case "+":
      operator = "+"
      num1 = parseFloat(displayVal);
      clearDisplay();
      break;
    case "-":
      operator = "-"
      num1 = parseFloat(displayVal);
      clearDisplay();      
      break;
    case "÷":
      operator = "÷"
      num1 = parseFloat(displayVal);
      clearDisplay();
      break;
    case "x":
      operator = "x"
      num1 = parseFloat(displayVal)
      clearDisplay();
      break;
    default:
      break;
  }
  if (finalVal === 0 && num1 !== 0)
      finalVal = num1;
};

//adding event listener to the operator btns, setting click to update num1 and operator variables
for (let i = 0; i < operatorBtns.length; i++) {
  operatorBtns[i].addEventListener("click", operator1, false);
}

//adding decimal functionality, checking to see if one is present already. 
decimalBtn.onclick = () => {
  if (!displayVal.includes("."))
    displayVal += "."

  displayValEl.innerText = displayVal;
}

//capturing the second value and performing the operation
equalsBtn.onclick = () => {  
  num2 = parseFloat(displayVal);
  
  switch (operator) {
    case '+':
      finalVal += num2;          
      break;
    case '-':
      finalVal -= num2;
      break;
    case 'x':
      finalVal *= num2;  
      break;
    case '÷':
      //for division by zero.
      if (num2 === 0)
      {
        alert("Cant divide by Zero");
        return;
      }
      finalVal /= num2;
      break;
    default:
      break;
  }
  displayVal = finalVal.toString()
  displayValEl.innerText = displayVal;
  num1 = num2;
  operator = "";
};

function clearValues() {
  num1 = 0;
  num2 = 0;
  finalVal = 0;
}

function clearDisplay() {
  displayVal = "0";
  displayValEl.innerText = displayVal;
}

clearBtn.onclick = () => {
  operator = "";
  clearValues();
  clearDisplay();
};

backspaceBtn.onclick = () => {
  let lengthOfDisplay = displayVal.length;
  displayVal = displayVal.slice(0, lengthOfDisplay - 1)

  var shouldResetCalc = (operator === "" && finalVal !== 0);
  
  if (displayVal === "")
    displayVal = "0";

  if (shouldResetCalc)
    finalVal = 0;
    
  displayValEl.innerText = displayVal;
};
button { min-width: 40px;}

.container > .container {margin-top: 1em;}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
<div class="container">
  <div class="xs-col-6 container">
    <div class="row xs-col-12">
      <button class="numBtn xs-col-4">7</button>
      <button class="numBtn xs-col-4">8</button>
      <button class="numBtn xs-col-4">9</button>
    </div>
    <div class="row xs-col-12">
      <button class="numBtn xs-col-4">4</button>
      <button class="numBtn xs-col-4">5</button>
      <button class="numBtn xs-col-4">6</button>
    </div>
    <div class="row xs-col-12">
      <button class="numBtn xs-col-4">1</button>
      <button class="numBtn xs-col-4">2</button>
      <button class="numBtn xs-col-4">3</button>
    </div>
    <div class="row xs-col-12">
      <button class="numBtn xs-col-4">0</button>
      <button id="decimal" class="xs-col-4">.</button>
      <button id="equals" class="xs-col-4">=</button>
    </div>
  </div>
  <div class="xs-col-6 container">
    <div class="row xs-col-12">
      <button class="operatorBtn divide">÷</button>
      <button class="operatorBtn muliply">x</button>
      <button class="operatorBtn subtract">-</button>
      <button class="operatorBtn add">+</button>
    </div>
    <div class="row xs-col-12">
      <button id="backspace">Del</button>
      <button id="clear">Clr</button>
    </div>
  </div>
</div>

<div id="display">
</div>

Upvotes: 0

Related Questions