skip to Main Content

I am currently working on my calculator school project.
It works fine if I use either a button or a keyboard input separately, however, something goes wrong in case of the mixed input, particularly when the Enter button is used.
Here is my full project: codepen.
The best way to describe problem is to provide a brief example, just 1 + 2.
Possible options:

  1. Use only buttons, the result is correct.
  2. Use only keys, the result is correct.
  3. Use mixed input and the = key, the result is correct.
  4. Use mixed input and the Enter key, the unexpected behaviour happens.

What is an unexpected behaviour?

  1. If the first number was entered by a button, but the second one by the keyboard, the first number is displayed instead of result.
  2. If the second or both numbers were introduced by buttons, the second number is displayed instead of result.

After both this cases, a consequent Enter press just multiply the displayed value instead of giving the error.

  1. Both numbers were entered by a keyboard, but an operator by a button: the program gives a right result but adds the operator after and freeze.

Here is how I handle a button click:

function handleButtonClick(event) {
    const buttonClicked = event.target;

    hideErrorIfEqualErrorDisplayed('mouse', buttonClicked);

    switch (buttonClicked) {
        case buttonDecimalPoint:
            handleDecimalPointButton();
            break;
        case buttonClear:
            clearAll();
            break;
        case buttonDelete:
            deleteLastSymbol();
            break;
        case buttonEqual:
            handleEqualSign();
            break;
        default:
            const classButton = buttonClicked.classList;
            const valueOfClickedButton = buttonClicked.textContent;

            if (classButton.contains('button-digit')) {
                handleDigitButton(valueOfClickedButton);
            } else if (classButton.contains('button-operator')) {
                handleOperatorButton(valueOfClickedButton);
            }
            break;
    }
}

That’s how I handle a key input:

function handleKeyDown(event) {
    const keyPressed = event.key;

    hideErrorIfEqualErrorDisplayed('keyboard', keyPressed);

    switch (keyPressed) {
        case SYMBOLS.DOT:
          if (isDecimalPointDisabled) {
            event.preventDefault();
          } else {
            handleDecimalPointButton();
          }
          break;
    
        case SYMBOLS.BACKSPACE:
          deleteLastSymbol();
          break;
    
        case SYMBOLS.ESCAPE:
          clearAll();
          break;
    
        case SYMBOLS.EQUAL:
        case SYMBOLS.ENTER:
            console.log(keyPressed);
          if (isZeroError) {
            event.preventDefault();
          } else {
            console.log(`current display: ${containerDisplay.textContent}`);
            console.log('Go to handleEqualSign()');
            handleEqualSign();
          }
          console.log('break');
          break;
    
        default:
          if (checkIfDigit(keyPressed)) {
            handleDigitButton(keyPressed);
          } else if (Object.values(OPERATORS).includes(keyPressed)) {
            if (areOperatorsDisabled) {
              event.preventDefault();
            } else {
              handleOperatorButton(keyPressed);
            }
          } else {
            event.preventDefault();
          }
          break;
      }
}

Here are other functions:

function handleEqualSign(){
    if (!isSecondNumber) {
        showErrorMessage(errorEqual);
        return;
    }

    console.log('Go to getResult()');
    getResult();
}

function hideErrorIfEqualErrorDisplayed(inputSource, symbolTyped) { // inputSource: keyboard or mouse
    if (isEqualError) {
        if (inputSource === 'mouse' && symbolTyped !== buttonEqual) {
            hideErrorMessage(errorEqual);
        } else if (inputSource === 'keyboard' && symbolTyped !== SYMBOLS.ENTER && symbolTyped !== SYMBOLS.EQUAL) {
            hideErrorMessage(errorEqual);
        }
    }
}

function handleOperatorButton(operatorValue) {
    disableButton(buttonDecimalPoint);

    if (isOperator) { // Display and use the last operator if the user has clicked more than one in a row
        deleteLastSymbol();
    }

    if (!isSecondNumber) {
        shiftFromFirstToOperator();   
    } else {
        getResult(isAfterEqualClick=false);
    }

    addToDisplay(operatorValue, gap=true)

    operator = operatorValue;  
}

function getResult(isAfterEqualClick=true) {  // isAfterEqualClick = true when we enter the function after the clicking '=' or pressing the 'Enter' key, false after clicking any other operator.
    const result = operate(parseFloat(number1), parseFloat(number2), operator);
    console.log(`getResult() arguments: ${parseFloat(number1)}, ${parseFloat(number2)}, ${operator} `);
    console.log(`Result: ${result}`);

    containerDisplay.textContent = parseFloat(result.toFixed(5));
    console.log(`containerDisplay.textContent: ${containerDisplay.textContent }`);
    
    number1 = result.toString();
    isResult = true;
    number2 = EMPTY; 
    isSecondNumber = false;

    if (isAfterEqualClick) {
        console.log('isAfterEqualClick = True');
        isFirstNumber = true;
        isOperator = false;
        operator = EMPTY;
    } else {
        console.log('isAfterEqualClick = False');
        shiftFromFirstToOperator();
    }

    if (!Number.isInteger(result)) {
        disableButton(buttonDecimalPoint);
    } else {
        enableDecimalPointIfDisabled();
    } 
}

function handleDigitButton(digitValue) { 
    if (isFirstNumber) { 
        handleFirstNumber(digitValue);
    } else if (isOperator && !isSecondNumber) { 
        handleStartOfSecondNumber(digitValue);
    } else {  
        handleContinueOfSecondNumber(digitValue);
    }
} 

function add(addend1, addend2) {
    return addend1 + addend2;
}

What I have already done:

  1. The issue is that this bug doesn’t occur in the inspect mode, it works properly there.
  2. Logging provides expected values so doesn’t help.
  3. The problem is the same in different browser, in the debug mode it disappears.

2

Answers


  1. I have just finished debugging your code.

    As a result, I think your heisenbug is caused by the enter key event. When you click the button, the button gets focus. Then, if you press the enter key, both handleKeyDown() and handleButtonClick() are executed continuously. As a result, your display div shows the correct result first and then the pressed button is re-pressed, causing errors.

    To fix this error, you can use the blur() method to remove the focus from the button.

    For example:

    function handleButtonClick(event) {
      const buttonClicked = event.target;
      buttonClicked.blur();
      /* ... */
    }
    
    Login or Signup to reply.
  2. Just check for event.pointerType to not be Mouse in the handleButtonClick function, it works. Thanks!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search