Skip to main content

Why does a keyboard input mess with the on screen display? (Calculator)

class Calculator {
    constructor(previousOperandTextElement, currentOperandTextElement) {
        this.previousOperandTextElement = previousOperandTextElement
        this.currentOperandTextElement = currentOperandTextElement
        this.clear()
    }


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

    delete() {
        this.currentOperand = this.currentOperand.toString().slice(0,-1)
    }

    appendNumber(number) {
        if(number === '.' && this.currentOperand.includes('.')) return
        this.currentOperand = this.currentOperand.toString() + number.toString()
    }

    chooseOperation(operation) {
        if(this.currentOperand === '') return
        if(this.previousOperand !== '') {
            this.compute()
        }
        this.operation = operation
        this.previousOperand = this.currentOperand
        this.currentOperand = ''
    }

    compute() {
        let computation
        const prev = parseFloat(this.previousOperand)
        const current = parseFloat(this.currentOperand)
        if(isNaN(prev) || isNaN(current)) return 1
        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 = ''
    }

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

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


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

const calculator = new Calculator(previousOperandTextElement, currentOperandTextElement)

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

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

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

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

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

document.onkeyup = e => {
    if(e.key == "1") {
        calculator.appendNumber(e.key)
        calculator.updateDisplay()
    } else if(e.key == "2") {
        calculator.appendNumber(e.key)
        calculator.updateDisplay()
    } else if(e.key == "3") {
        calculator.appendNumber(e.key)
        calculator.updateDisplay()
    } else if(e.key == "4") {
        calculator.appendNumber(e.key)
        calculator.updateDisplay()
    } else if(e.key == "5") {
        calculator.appendNumber(e.key)
        calculator.updateDisplay()
    } else if(e.key == "6") {
        calculator.appendNumber(e.key)
        calculator.updateDisplay()
    } else if(e.key == "7") {
        calculator.appendNumber(e.key)
        calculator.updateDisplay()
    } else if(e.key == "8") {
        calculator.appendNumber(e.key)
        calculator.updateDisplay()
    } else if(e.key == "9") {
        calculator.appendNumber(e.key)
        calculator.updateDisplay()
    } else if(e.key == "0") {
        calculator.appendNumber(e.key)
        calculator.updateDisplay()
    } else if(e.key == ".") {
        calculator.appendNumber(e.key)
        calculator.updateDisplay()
    } else if(e.key == "+" || e.key == "-" || e.key == "*" || e.key == "/") {
        if(e.key == "/"){
            let conversion = e.key;
            conversion = "÷";
            calculator.chooseOperation(conversion)
            calculator.updateDisplay()
        } else{
            calculator.chooseOperation(e.key)
            calculator.updateDisplay()
        }
    } else if(e.key == "Enter") {
        equalsButton.click()
    } else if(e.key == "Backspace") {
        calculator.delete()
        calculator.updateDisplay()
    }
}
*, *::before, *::after {
    box-sizing: border-box;
    font-family: Gotham Rounded, sans-serif;
    font-weight: normal;
}

body {
    padding: 0;
    margin: 0;
    background: linear-gradient(to right, #00AAFF, #00FF6C);
}

.calculator-grid {
    display: grid;
    justify-content: center;
    align-content: center;
    min-height: 100vh;
    grid-template-columns: repeat(4,100px);
    grid-template-rows: minmax(120px, auto) repeat(5,100px);
}

.calculator-grid > button {
    cursor: pointer;
    font-size: 2rem;
    border: 2px solid black;
    outline: none;
    background-color: rgba(255, 255, 255, .75)
}

.calculator-grid > button:hover {
    background-color: rgba(255, 255, 255, .9);
}

.span-two {
    grid-column: span 2;
}

.output {
    grid-column: 1/-1;
    background-color: rgba(0,0,0,.75);
    display: flex;
    align-items: flex-end;
    justify-content: space-around;
    flex-direction: column;
    padding: 10px;
    word-wrap: break-word;
    word-break: break-all;
}

.output .previous-operand {
    color: rgba(255, 255, 255,.75);
    font-size: 1.5rem;
}

.output .current-operand {
    color: white;
    font-size: 2.5rem;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Calculator</title>
    <link href="styles.css" rel="stylesheet">
    <script src="script.js" defer></script>
</head>
<body>
    <div class="calculator-grid">
        <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>
</body>
</html>

I think I'm just missing a step that properly integrates the keyboard with corresponding buttons on screen.

I made a basic calculator. Works just fine, except the weirdest thing happens: When I hit enter, AFTER using the buttons on the screen, the display goes blank. But if I use the keyboard FIRST to get an answer, then click the buttons to get another answer, display is fine until I try to use the keyboard again. After some messing around I found that, for some reason, I have to click my screen once, anywhere not on the calculator, before pressing enter works. Otherwise, the display doesn't show.

So it appears to me that im somehow "disconnecting" (for lack of a better term) the display between the keyboard and the on-screen buttons, which causes my display to blank, and I have to "reconnect" it by clicking on the screen outside the calculator in order for hitting enter to not blank.

Any insight would be greatly appreciated, language is javascript. Code has been provided after requested!

Answer

When you click on an button, it gets the focus. The default action of the enter key when a button is focused is to click on that button again, so the enter key is repeating whatever you last clicked on, before it performs the action you defined in the document.onkeyup function.

The solution is to prevent the default action. This default happens in the keydown event, so you need to add an event listener for that event that calls e.preventDefault()

class Calculator {
  constructor(previousOperandTextElement, currentOperandTextElement) {
    this.previousOperandTextElement = previousOperandTextElement
    this.currentOperandTextElement = currentOperandTextElement
    this.clear()
  }


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

  delete() {
    this.currentOperand = this.currentOperand.toString().slice(0, -1)
  }

  appendNumber(number) {
    if (number === '.' && this.currentOperand.includes('.')) return
    this.currentOperand = this.currentOperand.toString() + number.toString()
  }

  chooseOperation(operation) {
    if (this.currentOperand === '') return
    if (this.previousOperand !== '') {
      this.compute()
    }
    this.operation = operation
    this.previousOperand = this.currentOperand
    this.currentOperand = ''
  }

  compute() {
    let computation
    const prev = parseFloat(this.previousOperand)
    const current = parseFloat(this.currentOperand)
    if (isNaN(prev) || isNaN(current)) return 1
    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 = ''
  }

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

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


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

const calculator = new Calculator(previousOperandTextElement, currentOperandTextElement)

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

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

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

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

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

document.addEventListener("keydown", e => e.preventDefault());

document.onkeyup = e => {
  if (e.key == "1") {
    calculator.appendNumber(e.key)
    calculator.updateDisplay()
  } else if (e.key == "2") {
    calculator.appendNumber(e.key)
    calculator.updateDisplay()
  } else if (e.key == "3") {
    calculator.appendNumber(e.key)
    calculator.updateDisplay()
  } else if (e.key == "4") {
    calculator.appendNumber(e.key)
    calculator.updateDisplay()
  } else if (e.key == "5") {
    calculator.appendNumber(e.key)
    calculator.updateDisplay()
  } else if (e.key == "6") {
    calculator.appendNumber(e.key)
    calculator.updateDisplay()
  } else if (e.key == "7") {
    calculator.appendNumber(e.key)
    calculator.updateDisplay()
  } else if (e.key == "8") {
    calculator.appendNumber(e.key)
    calculator.updateDisplay()
  } else if (e.key == "9") {
    calculator.appendNumber(e.key)
    calculator.updateDisplay()
  } else if (e.key == "0") {
    calculator.appendNumber(e.key)
    calculator.updateDisplay()
  } else if (e.key == ".") {
    calculator.appendNumber(e.key)
    calculator.updateDisplay()
  } else if (e.key == "+" || e.key == "-" || e.key == "*" || e.key == "/") {
    if (e.key == "/") {
      let conversion = e.key;
      conversion = "÷";
      calculator.chooseOperation(conversion)
      calculator.updateDisplay()
    } else {
      calculator.chooseOperation(e.key)
      calculator.updateDisplay()
    }
  } else if (e.key == "Enter") {
    equalsButton.click()
  } else if (e.key == "Backspace") {
    calculator.delete()
    calculator.updateDisplay()
  }
}
*,
*::before,
*::after {
  box-sizing: border-box;
  font-family: Gotham Rounded, sans-serif;
  font-weight: normal;
}

body {
  padding: 0;
  margin: 0;
  background: linear-gradient(to right, #00AAFF, #00FF6C);
}

.calculator-grid {
  display: grid;
  justify-content: center;
  align-content: center;
  min-height: 100vh;
  grid-template-columns: repeat(4, 100px);
  grid-template-rows: minmax(120px, auto) repeat(5, 100px);
}

.calculator-grid>button {
  cursor: pointer;
  font-size: 2rem;
  border: 2px solid black;
  outline: none;
  background-color: rgba(255, 255, 255, .75)
}

.calculator-grid>button:hover {
  background-color: rgba(255, 255, 255, .9);
}

.span-two {
  grid-column: span 2;
}

.output {
  grid-column: 1/-1;
  background-color: rgba(0, 0, 0, .75);
  display: flex;
  align-items: flex-end;
  justify-content: space-around;
  flex-direction: column;
  padding: 10px;
  word-wrap: break-word;
  word-break: break-all;
}

.output .previous-operand {
  color: rgba(255, 255, 255, .75);
  font-size: 1.5rem;
}

.output .current-operand {
  color: white;
  font-size: 2.5rem;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Calculator</title>
  <link href="styles.css" rel="stylesheet">
  <script src="script.js" defer></script>
</head>

<body>
  <div class="calculator-grid">
    <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>
</body>

</html>

Comments