utkuonursahin
utkuonursahin

Reputation: 469

JavaScript gives NaN error on the page but variable is actually a number

I am trying to make a basic calculator with JS. It's working for first step. I mean it calculates 1+3 = 4 but then it can't calculate 4+2 for example. Actually it calculates it on the console but I can't show it on the webpage, so DOM. So do you have any ideas why is that happening?

const operations = {
    '+': (a, b) => a + b,
    '-': (a, b) => a - b,
    '*': (a, b) => a * b,
    '/': (a, b) => a / b
}

const doOpr = function (sign) {
    labelUser.value += `${sign}`

    // const deleteFn = function () {
    //     labelUser.value = labelUser.value.slice(0, labelUser.value.length - 1)
    // }
    // btnDelete.addEventListener('click', deleteFn)

    const equalFn = function (sign) {
        let numbers = labelUser.value.split(`${sign}`)
        //console.log(numbers)
        const operation = operations[`${sign}`]
        let result = operation(+numbers[0], +numbers[1])
        labelUser.value = result

        //console.log(typeof result, numbers) //This line proves that result is a number. Not a NaN. 
        //Also it proves numbers array contains only two elements.

        return
    }
    btnEqual.addEventListener('click', equalFn.bind(null, sign))
}

btnPlus.addEventListener('click', doOpr.bind(null, '+'))

btnExtr.addEventListener('click', doOpr.bind(null, '-'))

const btnPlus = document.querySelector('.btn-plus')
const btnExtr = document.querySelector('.btn-extr')
const btnEqual = document.querySelector('.btn-equal')
const btnDelete = document.querySelector('.btn-delete')
const labelUser = document.querySelector('.user__input')
const form = document.querySelector('.user__form')

form.reset()
const operations = {
    '+': (a, b) => a + b,
    '-': (a, b) => a - b,
    '*': (a, b) => a * b,
    '/': (a, b) => a / b
}

const doOpr = function (sign) {
    labelUser.value += `${sign}`

    // const deleteFn = function () {
    //     labelUser.value = labelUser.value.slice(0, labelUser.value.length - 1)
    // }
    // btnDelete.addEventListener('click', deleteFn)

    const equalFn = function (sign) {
        let numbers = labelUser.value.split(`${sign}`)
        const operation = operations[`${sign}`]
        let result = operation(+numbers[0], +numbers[1])
        labelUser.value = result
        console.log(typeof result, numbers)
        return
    }
    btnEqual.addEventListener('click', equalFn.bind(null, sign))
}

btnPlus.addEventListener('click', doOpr.bind(null, '+'))

btnExtr.addEventListener('click', doOpr.bind(null, '-'))
*,
*::after,
*::before {
  margin: 0;
  padding: 0;
  box-sizing: inherit;
}

html {
  font-size: 62.5%;
  box-sizing: border-box;
}

.container {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 25%;
  height: 85%;
  background-color: olive;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(6, 1fr);
}

.user__form {
  grid-row: 1/3;
  grid-column: 1/-1;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

.user__input {
  width: 90%;
  height: 80%;
  font-size: 6rem;
  text-align: right;
  padding: 0 3rem;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Basic Calculator</title>
    <link rel="stylesheet" href="./css/style.css">
</head>

<body>
    <div class="container">
        <form class="user__form">
            <input type="text" class="user__input" placeholder="Entry num">
        </form>

        <button class="btn-plus">
            addition
        </button>

        <button class="btn-extr">
            extraction
        </button>

        <button class="btn-equal">
            equal
        </button>

        <button class="btn-delete">
            delete
        </button>

    </div>
    <script src="./script.js"></script>
</body>

</html>

enter image description here

Here you can see, I entered first 2+1 and it calculated and showed it but then I tried same operation again, it calculated but can't showed on the page.

Upvotes: 2

Views: 423

Answers (1)

VLAZ
VLAZ

Reputation: 28970

The problem right now is happens if you:

  1. Type in "2".
  2. Press the "addition" button.
  3. Type in "3".
  4. Press the "equal" button.
  5. Press the "addition" button.
  6. Type "4".
  7. Press the "equal" button.

Step 2. will add an event listener to the equal button and on click (step 4.) it will do the logic for addition. However, the listener remains. At step 5. another listener is added. At step 7. two event listeners fire off: the first one will do the addition (5 + 4 = 9) the second one will attempt to do addition again by splitting on + and summing the two values. However, the value is already just "9", so this results in a problem because there are not two operands:

const operations = {
    '+': (a, b) => a + b,
}

const value = "9";

const numbers = value.split("+");
const a = +numbers[0];
const b = +numbers[1];

console.log(numbers, a, b);

console.log(operations["+"](a, b))

Since the problem is multiple event listeners, you can make an event listener that self-destructs after it fires:

const btnPlus = document.querySelector('.btn-plus')
const btnExtr = document.querySelector('.btn-extr')
const btnEqual = document.querySelector('.btn-equal')
const btnDelete = document.querySelector('.btn-delete')
const labelUser = document.querySelector('.user__input')
const form = document.querySelector('.user__form')

form.reset()
const operations = {
    '+': (a, b) => a + b,
    '-': (a, b) => a - b,
    '*': (a, b) => a * b,
    '/': (a, b) => a / b
}

const doOpr = function (sign) {
    labelUser.value += `${sign}`

    const equalFn = function(sign) { //equalFN should return a function 
      return function equal() { //give the function a name --+
        let numbers = labelUser.value.split(`${sign}`) //    |
        const operation = operations[`${sign}`] //           |
        let result = operation(+numbers[0], +numbers[1]) //  |
        console.log(typeof result, result, numbers) //       |
        labelUser.value = result //                          |
        btnEqual.removeEventListener("click", equal); //<----+ use the name to remove it
        return
      }
    }
    btnEqual.addEventListener('click', equalFn(sign))
}


btnPlus.addEventListener('click', doOpr.bind(null, '+'))

btnExtr.addEventListener('click', doOpr.bind(null, '-'))
*,
*::after,
*::before {
  margin: 0;
  padding: 0;
  box-sizing: inherit;
}

html {
  font-size: 62.5%;
  box-sizing: border-box;
}

.container {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 25%;
  height: 85%;
  background-color: olive;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(6, 1fr);
}

.user__form {
  grid-row: 1/3;
  grid-column: 1/-1;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

.user__input {
  width: 90%;
  height: 80%;
  font-size: 6rem;
  text-align: right;
  padding: 0 3rem;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Basic Calculator</title>
    <link rel="stylesheet" href="./css/style.css">
</head>

<body>
    <div class="container">
        <form class="user__form">
            <input type="text" class="user__input" placeholder="Entry num">
        </form>

        <button class="btn-plus">
            addition
        </button>

        <button class="btn-extr">
            extraction
        </button>

        <button class="btn-equal">
            equal
        </button>

        <button class="btn-delete">
            delete
        </button>

    </div>
    <script src="./script.js"></script>
</body>

</html>

However, having one-off event listeners is generally a bad idea. It means that correct operations is dependent on sequence of actions. In fact, right now, it is not possible to type in "2+3" and calculate it, you have to press the "addition" button to register the additiion handler.

Instead, this can be decoupled. If you only expect a single operator then with a very small modification, you can make the equals handler only check for the operands defined in operations. If you have it, then use the normal logic to split and process the input.

This allows you to keep the equal operation separate and not rely on clicking on "addition" or "subtraction" before evaluating the result. Therefore, those two buttons become very simple and only add a "+" or "-" to the entry field. However, a user just typing "2+3" using their keyboard also works and will be evaluated as 5.

const btnPlus = document.querySelector('.btn-plus')
const btnExtr = document.querySelector('.btn-extr')
const btnEqual = document.querySelector('.btn-equal')
const btnDelete = document.querySelector('.btn-delete')
const labelUser = document.querySelector('.user__input')
const form = document.querySelector('.user__form')

form.reset()
const operations = {
    '+': (a, b) => a + b,
    '-': (a, b) => a - b,
    '*': (a, b) => a * b,
    '/': (a, b) => a / b
}

const doOpr = function (sign) {
    labelUser.value += `${sign}`
}

//extract equalFn as a separate handler
const equalFn = function () {
    let sign;
    //search the user input for any supported operation
    //and set the sign to that operation
    for (const op of Object.keys(operations)) {
      if (labelUser.value.includes(op)) {
          sign = op;
          break;
        }
    }
    if (sign === undefined)
      return; //cannot be calculated
    
    //use the previous logic to split the input and process it
    let numbers = labelUser.value.split(sign)
    const operation = operations[sign]
    let result = operation(+numbers[0], +numbers[1])
    console.log(typeof result, result, numbers)
    labelUser.value = result
    return
}

btnPlus.addEventListener('click', doOpr.bind(null, '+'))
btnExtr.addEventListener('click', doOpr.bind(null, '-'))

//only add equalFn a single time as a handler
btnEqual.addEventListener('click', equalFn)
*,
*::after,
*::before {
  margin: 0;
  padding: 0;
  box-sizing: inherit;
}

html {
  font-size: 62.5%;
  box-sizing: border-box;
}

.container {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 25%;
  height: 85%;
  background-color: olive;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(6, 1fr);
}

.user__form {
  grid-row: 1/3;
  grid-column: 1/-1;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

.user__input {
  width: 90%;
  height: 80%;
  font-size: 6rem;
  text-align: right;
  padding: 0 3rem;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Basic Calculator</title>
    <link rel="stylesheet" href="./css/style.css">
</head>

<body>
    <div class="container">
        <form class="user__form">
            <input type="text" class="user__input" placeholder="Entry num">
        </form>

        <button class="btn-plus">
            addition
        </button>

        <button class="btn-extr">
            extraction
        </button>

        <button class="btn-equal">
            equal
        </button>

        <button class="btn-delete">
            delete
        </button>

    </div>
    <script src="./script.js"></script>
</body>

</html>

Upvotes: 2

Related Questions