4
\$\begingroup\$

I'm just starting out with code. I created a very basic JavaScript calculator. While writing, I couldn't help but think that there must be easier, faster and less redundant ways to write this. For example, when I have to update the display, I have to call the refreshScreen function, which then takes the previous HTML, adds the new stuff to it, and then sets it as the new HTML.

I guess what I'm trying to say is, how can I make the following code less redundant, and are there any libraries that can help me accomplish the same thing in an easier/better/more appreciated way?

It would also be great if I could get some comments on the style of the code. I really want to know what the best coding practices are.

var ans = "0"; // current answer
var pressed = "0"; // keeps track of everything that has been pressed
var resultsScreen = $("#results");
var needsNumber = true; // keeps track of whether or not an operation was selected previously
var allButtons = $("button");
var decimalUsed = false; // makes sure that the user uses the decimal only once

$(document).ready(function(){
    allButtons.on("click", function(){
        var content = $(this).html();
        // special cases
        switch (content) {
            case "=":
                computeAnswer();
                break;
            case "AC":
                pressed = "0";
                break;
            case "CE":
                removeLastPressed();
                break;
            case "Ans":
                if (lastPressedOperation()) {
                    pressed += ans;
                }
                if (pressed == "0") { // if refreshed by pressing AC
                    pressed = ans;
                }
                break;
            default:
                var val = $(this).attr("value");
                console.log("You pressed " + val);
                if (val == null) { // if its an operation
                    if (!lastPressedOperation()) { // if allowed to press operation again
                        pressed += content;
                    }
                } else {
                    if (!(val == "." && decimalUsed)) {
                        if (pressed == "0") {
                            pressed = val;
                        } else {
                            pressed += val;
                        }
                        if (val ==".") {
                            decimalUsed = true;
                        }
                    }
                }
                console.log(pressed);
                break;
        }
        refreshDisplay();
    });
});

// post: displays the most current equation or answer
function refreshDisplay(){
    resultsScreen.html(pressed);
    console.log("Your current equation is " + pressed);
}

// post: calculates the answer;
// displays "syntax error" if there is one and alerts the user
function computeAnswer() {
    var val = 0;
    if (lastPressedOperation()) {
        pressed = "Syntax error!";
        refreshDisplay();
        alert("Syntax Error!");
    } else {
        pressed = pressed.replace("x", "*");
        pressed = eval(pressed) + "";
        ans = pressed;
        refreshDisplay();
    }
}

// post: returns true if the last pressed button was an operation; false otherwise
function lastPressedOperation() {
    var lastPressed = pressed.charAt(pressed.length - 1);
    return lastPressed == "x" || lastPressed == "+" || lastPressed == "-" || lastPressed == "/" || lastPressed == "%";
}

// post: removes the last pressed button from the screen and memory
function removeLastPressed() {
    if (pressed.length > 0) {
        pressed = pressed.substring(0, pressed.length - 1); // forget the last pressed button
        if (pressed == "") {
            pressed = "0";
        }
    }
}
h1 {
    text-align: center;
    color: white;
}

#box {
    background: #005A31;
    position: relative;
    top: 10vh;
    width: 50%;
}

#results {
    background: #CBE32D;
    margin: 1%;
    text-align: right;
    font-size: 300%;
}

.buttons {
    background: #A8CD1B;
    margin: 1%;
    text-align: center;
    display: inline-block;
    border: hidden;
    border-radius: 20%;
    font-size: 200%;
    padding: 4%;
}

.buttons:hover {
    cursor: pointer;
    background: #CBE32D; !important
}

.buttons:active {
    background: #005A99;
}

.operations {
    background: #F3FAB6;
}

button {
    float: none !important;
}

#row-container{
    text-align: center;
}

#equals {
    border: solid;
    border-color: #092352;
    border-width: 7px;
}
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"/>
<link rel="stylesheet" href="style.css"/>
<html>
    <div class="container-fluid" id="box">
        <h1>JavaScript Calculator</h1>
        <div id="results">0</div>
        <div id="row-container">
            <div class="row">
                <button id="AC" class="buttons col-md-2 operations ">AC</button>
                <button id="CE" class="buttons col-md-2 operations ">CE</button>
                <button id="percent" class="buttons col-md-2 operations ">%</button>
                <button id="subtract" class="buttons operations col-md-3 ">-</button>
            </div>
            <div class="row">
                <button value="7" class="buttons col-md-2 ">7</button>
                <button value="8" class="buttons col-md-2 ">8</button>
                <button value="9" class="buttons col-md-2 ">9</button>
                <button id="multiply" class="buttons operations col-md-3 ">x</button>
            </div>
            <div class="row">
                <button value="4" class="buttons col-md-2 ">4</button>
                <button value="5" class="buttons col-md-2 ">5</button>
                <button value="6" class="buttons col-md-2 ">6</button>
                <button id="divide" class="buttons operations col-md-3 ">/</button>
            </div>
            <div class="row">
                <button value="1" class="buttons col-md-2 ">1</button>
                <button value="2" class="buttons col-md-2 ">2</button>
                <button value="3" class="buttons col-md-2 ">3</button>
                <button id="add" class="buttons operations col-md-3 ">+</button>
            </div>
            <div class="row">
                <button value="." class="buttons col-md-2 ">.</button>
                <button value="0" class="buttons col-md-2 ">0</button>
                <button id="ans" class="buttons col-md-2  operations">Ans</button>
                <button id="equals" class="buttons col-md-3 operations ">=</button>
            </div>
        </div>
    </div> 
</html>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>

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

Here is the jsfiddle: https://jsfiddle.net/qLc0st3z/

\$\endgroup\$
1
  • 1
    \$\begingroup\$ You can only use the decimal once, so something like 1.15 + 1.64 will not work, as you cannot add another decimal \$\endgroup\$ Jun 1, 2016 at 11:55

1 Answer 1

2
\$\begingroup\$

Edit: you should wrap it in this:

;(function () {
  'use strict'

  // all your code goes here

}())

The ;(function () { }()) bit is known as an IIFE (immediately-invoked function expression). It stops variables from leaking into global scope. This is only a problem in browser JavaScript; in Node.js top-level variables don't leak into global scope.

The 'use strict' directive tells the engine to run the code in strict mode. In a nutshell, strict mode throws more errors, prohibits the dreaded with statement, and throws an error if you try to assign to an undeclared variable. It's useful for finding mistyped variables:

;(function () {
  let foo = 42
  fooo = 43
  console.log('foo is', foo) // foo is 42
}())

;(function () {
  'use strict'

  let foo = 42
  fooo = 43 // Throws an error
  console.log('foo is', foo)
}())

You should use strict mode all the time, in development and production, unless you need a 0.01% edge case where part of your file has to be non-strict. If you use Babel it outputs code in strict mode.


HTML structure. The usual way is:

<html>
  <head>
    <link rel="stylesheet" href="foo.css">
    <title>Blah blah blah</title>
  </head>
  <body>
    <!-- main page content goes here -->
    <script src="foo.js"></script>
  </body>
</html>

But the <html>, <head>, and <body> tags are optional, so you cam leave them out if you want.


.buttons:hover {
    cursor: pointer;
    background: #CBE32D; !important
}

The !important goes before the semicolon. But try to avoid using !important, as it can make things messy.

Also, you can set cursor: pointer on .buttons instead of on .buttons:hover. There's no visual difference, except for the specificity of the selectors.


#equals {
    border: solid;
    border-color: #092352;
    border-width: 7px;
}

can be written with the shorthand border property:

#equals {
    border: 7px solid #092352;
}

return lastPressed == "x" || lastPressed == "+" || lastPressed == "-" || lastPressed == "/" || lastPressed == "%";

can be written as:

return ["x", "+", "-", "/", "%"].indexOf(lastPressed) > 0

or (ES2016 and newer):

return ["x", "+", "-", "/", "%"].includes(lastPressed)

Some people prefer not to indent switch statements, so instead of this:

switch (foo) {
    case 1:
        doSomething()
        break
    case 2:
        doSomethingElse()
        break
}

do this:

switch (foo) {
case 1:
    doSomething()
    break
case 2:
    doSomethingElse()
    break
}

It can avoid over-indentation. It's just a matter of style.


Avoid the == operator most of the time, use === instead. == does loose equality, which means it converts things between different types, and it can do the opposite of what you expect. === does strict equality, which means it returns true if the two things are the same (except for NaN). It does what you think it'll do.

There is one good reason for using ==: foo == null is a shorter way of writing foo === null || foo === undefined.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.