1
\$\begingroup\$

Like basically everyone else, I made a vanilla JS calculator as practice and am hoping to get some feedback on it. I struggled a little bit with the actual logic, especially at the beginning, in terms of the good way to store the input and then using that as an operand while also getting chaining to work, both with the equals button and the operator buttons themselves (ex. Doing 2 + 2 and then instead of hitting the equals, hitting the plus button again and then another number). Also I wasn't sure the best way to take numbers as input while also allowing for a new number to be appended onto the current input, so I currently have my current input as a string.

Please disregard the percent button, which hasn't been implemented yet. Everything else should work properly. CSS isn't my strong point, I just kind of threw it together to more or less test the JS.

const controls = document.querySelector('.controls');
const output = document.querySelector('.output-text');
const numbers = document.querySelectorAll('.number');
const operators = document.querySelectorAll('.operator');
const equals = document.querySelector('.equals');
const decimal = document.querySelector('.decimal');
const options = document.querySelectorAll('.option');

//Model
const model = {
    tempResult: 0,
    firstOperand: 0,
    secondOperand: 0,
    currentInput: '',
    operator: '',
    executed: false,
    add() {
        model.tempResult = parseFloat(model.firstOperand) + parseFloat(model.secondOperand);
    },
    subtract() {
        model.tempResult = parseFloat(model.firstOperand) - parseFloat(model.secondOperand);
    },
    multiply() {
        model.tempResult = parseFloat(model.firstOperand) * parseFloat(model.secondOperand);
    },
    divide() {
        model.tempResult = parseFloat(model.firstOperand) / parseFloat(model.secondOperand);
    },
}

//View
const view = {
    updateDisplay() {
        output.textContent = model.currentInput;
    },
    showResult() {
        if (String(model.tempResult).length > 15 && String(model.tempResult).includes('.')) {
            output.textContent = parseFloat(model.tempResult.toFixed(8));
        }
        else {
            output.textContent = model.tempResult;
        }
    },
    enableDecimal(event) {
        decimal.disabled = false;
    },
    disableDecimal(event) {
        decimal.disabled = true;
    },
    negate() {
        output.textContent = output.textContent * -1;
    }
}

//Controller
const controller = {
    setUpHandlers() {
        numbers.forEach(function (element) {
            element.addEventListener('click', controller.clickedNumber);
        });
        operators.forEach(function (element) {
            element.addEventListener('click', controller.clickedOperator);
        });
        equals.addEventListener('click', this.execute);
        options.forEach(function (element) {
            element.addEventListener('click', controller.clickedOption);
        });
    },
    clickedNumber(event) {
        if (model.currentInput) {
            if (String(model.currentInput).length < 15 && model.executed == false) {
                model.currentInput += event.target.value;
                view.updateDisplay();
            }
        }
        else {
            if (event.target.value !== '0') {
                model.currentInput += event.target.value;
                view.updateDisplay();
            }
        }
    },
    clickedOperator(event) {
        if (!model.executed) {
            controller.execute();
        }
        view.enableDecimal();
        model.executed = false;
        model.operator = event.target.value;
        model.currentInput = output.textContent;
        model.firstOperand = model.currentInput;
        model.currentInput = '';
    },
    execute() {
        model.executed = true;
        view.disableDecimal();
        switch (model.operator) {
            case '+':
                model.secondOperand = model.currentInput;
                model.add();
                view.showResult();
                model.firstOperand = model.tempResult;
                break;
            case '-':
                model.secondOperand = model.currentInput;
                model.subtract();
                view.showResult();
                model.firstOperand = model.tempResult;
                break;
            case 'X':
                model.secondOperand = model.currentInput;
                model.multiply();
                view.showResult();
                model.firstOperand = model.tempResult;
                break;
            case '/':
                model.secondOperand = model.currentInput;
                model.divide();
                view.showResult();
                model.firstOperand = model.tempResult;
                break;
        }
    },
    clickedOption(event) {
        let optionClicked = event.target.value;
        switch (optionClicked) {
            case 'AC':
                controller.allClear();
                break;
            case '.':
                view.disableDecimal(event);
                model.currentInput += '.';
                view.updateDisplay();
                break;
            case '+/-':
                view.negate();
        }
    },
    allClear() {
        model.executed = false;
        model.tempResult = 0;
        model.firstOperand = 0;
        model.secondOperand = 0;
        model.currentInput = '';
        output.textContent = '0';
        model.operator = '';
        view.enableDecimal();
    }
}

controller.setUpHandlers();
body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    height: 500px;
}

.calculator {
    display: flex;
    border: 2px solid black;
    width: 300px;
    height: 350px;
    align-items: center;
    justify-content: space-evenly;
    flex-direction: column;
    border-radius: 15px;
    box-shadow: 4px 4px 4px 4px lightgray;
}

.output-box {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 250px;
    height: 50px;
    border: 2px solid black;
    border-radius: 15px;
}

.controls {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-content: space-around;
    width: 250px;
    height: 245px;
    border: 2px solid black;
    border-radius: 15px;
    padding: 4px;

}

.row {
    display: flex;
    justify-content: space-evenly;
    align-items: center;
    height: 100px;
    width: 250px;
}

.row > div {
    border: 2px solid black;
}

button[value="0"]{
    flex-grow: 2;
}

.output-box p {
    width: 200px;
    margin: 10px 10px;
    text-align: right;
}

button {
    width: 50px;
    height: 35px;
    border-radius: 3px;
    width: 50px;
    margin: 6px;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <title>Calculator</title>
    <link rel="stylesheet" href="styles.css">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
    <div id="header"><h2>JavaScript Calculator</h2></div>
    <div class="calculator">
        <div class="output-box">
            <p class="output-text">0</p>
        </div>
        <div class="controls">
            <div class="row" id="1">
                <button class="option" value="AC">AC</button>
                <button class="option" value="+/-">+/-</button>
                <button class="percent" value="%">%</button>
                <button class="operator" value="/">/</button>
            </div>
            <div class="row" id="2">
                <button class="number" value="7">7</button>
                <button class="number" value="8">8</button>
                <button class="number" value="9">9</button>
                <button class="operator" value="X">X</button>
            </div>
            <div class="row" id="3">
                <button class="number" value="4">4</button>
                <button class="number" value="5">5</button>
                <button class="number" value="6">6</button>
                <button class="operator" value="-">-</button>
            </div>
            <div class="row" id="4">
                <button class="number" value="1">1</button>
                <button class="number" value="2">2</button>
                <button class="number" value="3">3</button>
                <button class="operator" value="+">+</button>
            </div>
            <div class="row" id="5">
                <button class="number" value="0">0</button>
                <button class="option decimal" value=".">.</button>
                <button class="equals" value="=">=</button>
            </div>
        </div>
    </div>
    <script src="script.js"></script>
</body>

</html>

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.