Simple JS calculator implementation code and js calculator code

Source: Internet
Author: User
Tags function calculator

Simple JS calculator implementation code and js calculator code

Look at the calculators in your cell phone. They are divided into common calculators and scientific calculators.


If you think your head is not big enough, you can implement a normal version (supporting basic continuous operations such as positive, negative, addition, subtraction, multiplication, division, and not providing the brackets function)

See the figure below:


I. Knowledge preparation

1 + 1 =?

Normally, we can see that this expression knows how to calculate and how to calculate the result.

However, unlike computers, computers cannot recognize this string of expressions. They can only recognize specific rules: prefix expression + 1 or suffix expression 1 +

Example

(3 + 4) × 5-6 is the infix expression.
-× + 3 4 5 6 prefix expression
3 4 + 5 × 6-suffix expression

Therefore, to realize the automatic operation of the program, we need to convert the input data into a prefix or suffix expression.

The concepts of prefix, infix, suffix expression, and the method of mutual conversion are not mentioned here. This blog post is clear.

Therefore, the extension expression is used in the implementation of this calculator. Refer to the above articles and focus on these two algorithms:

Similar to a prefix expression, follow these steps:
(1) initialize two stacks: Operator stack S1 and stack S2 that stores intermediate results;
(2) scan the infix expression from left to right;
(3) press the operand into S2;
(4) When an operator is encountered, compare its priority with the S1 stack top operator:
(4-1) If S1 is null, or the top-end operator of the stack is left brackets (), the operator is directly added to the stack;
(4-2) Otherwise, if the priority is higher than the top operator of the stack, the operator is also pushed to S1 (note that the conversion to prefix expressions has a higher or the same priority, this does not include the same situation );
(4-3) otherwise, the operator at the top of the S1 stack will pop up and press it into S2, and then go to (4-1) again to compare with the operator at the top of the new stack in S1;
(5) parentheses:
(5-1) if it is a left brace "(", Press it directly into S1;
(5-2) if it is the right brace ")", the operator at the top of the S1 stack is popped up and pushed to S2 until the left brace is encountered. At this time, this pair of parentheses is discarded;
(6) Repeat steps (2) to (5) until the rightmost side of the expression;
(7) pop up the remaining operators in S1 and press them into S2;
(8) The elements in S2 are displayed and output in sequence. The reverse order of the result is the suffix expression corresponding to the infix expression (no reverse order is required when the prefix expression is converted ).

Similar to prefix expressions, the order is from left to right:
From left to right scan expression. When a number is encountered, the number is pushed into the stack. When an operator is encountered, the two numbers at the top of the stack are displayed, use operators to calculate them (the top element of the op stack) and import the result to the stack. Repeat the above process until the rightmost end of the expression, the final calculated value is the result of the expression.
For example, the suffix expression "3 4 + 5 × 6 -":
(1) scan from left to right and press 3 and 4 into the stack;
(2) When the + operator is encountered, 4 and 3 (4 is the top element of the stack, 3 is the top element of the stack, and note that it is compared with the prefix expression) are displayed, and the value of 3 + 4 is calculated, 7, and then 7 into the stack;
(3) add 5 to the stack;
(4) The next step is the X operator. Therefore, 5 and 7 are displayed, and 7 × 5 = 35 is calculated. The 35 is added to the stack;
(5) Add 6 to the stack;
(6) Finally, the-operator calculates the value of 35-6, that is, 29, and the final result is obtained.

II. Implementation Process

The first step is to build the Page Structure of the calculator. It is not a scientific calculator. It only provides basic computing functions, but can also perform operations instantly to display the complete infix expression, save the previous operation record.

First, I want to implement the decimal point function. However, the existence of the decimal point puts pressure on data storage and data display. This function is simply canceled.

1. Page Structure:

<h5> Calculation calculation </ h5>
  <!-Calculator->
  <div class = "calc-wrap">
    <div class = "calc-in-out">
      <!-Previous operation record->
      <p class = "calc-history" title = ""> </ p>
      <!-Input data->
      <p class = "calc-in"> </ p>
      <!-Output operation result->
      <p class = "calc-out active"> </ p>
    </ div>
    <table class = "calc-operation">
      <thead> </ thead>
      <tbody>
        <tr>
          <td data-ac = "cls" class = "cls"> C </ td>
          <td data-ac = "del"> ← </ td>
          <td data-ac = "sq"> x <sup> 2 </ sup> </ td>
          <td data-ac = "mul"> × </ td>
        </ tr>
        <tr>
          <td data-val = "7"> 7 </ td>
          <td data-val = "8"> 8 </ td>
          <td data-val = "9"> 9 </ td>
          <td data-ac = "div"> ÷ </ td>
        </ tr>
        <tr>
          <td data-val = "4"> 4 </ td>
          <td data-val = "5"> 5 </ td>
          <td data-val = "6"> 6 </ td>
          <td data-ac = "plus"> + </ td>
        </ tr>
        <tr>
          <td data-val = "1"> 1 </ td>
          <td data-val = "2"> 2 </ td>
          <td data-val = "3"> 3 </ td>
          <td data-ac = "minus">-</ td>
        </ tr>
          <td data-ac = "per">% </ td>
          <td data-val = "0"> 0 </ td>
          <td data-ac = "dot">. </ td>
          <td data-ac = "eq" class = "eq"> = </ td>
      </ tbody>
    </ table>
  </ div>
2. Combine a little style:

body {
  padding: 20px;
  font-family: Arial;
}

.calc-wrap {
  width: 300px;
  border: 1px solid #ddd;
  border-radius: 3px;
}


.calc-operation {
  width: 100%;
  border-collapse: collapse;
}

.calc-in-out {
  width: 100%;
  padding: 10px 20px;
  text-align: right;
  box-sizing: border-box;
  background-color: rgba (250, 250, 250, .9);
}
.calc-in-out p {
  overflow: hidden;
  margin: 5px;
  width: 100%;
}
.calc-history {
  margin-left: -20px;
  font-size: 18px;
  color: #bbb;
  border-bottom: 1px dotted #ddf;
  min-height: 23px;
}

.calc-in,
.calc-out {
  font-size: 20px;
  color: # 888;
  line-height: 39px;
  min-height: 39px;
}

.calc-in {
  color: # 888;
}
.calc-out {
  color: #ccc;
}

.calc-in.active,
.calc-out.active {
  font-size: 34px;
  color: # 666;
}

.calc-operation td {
  padding: 10px;
  width: 25%;
  text-align: center;
  border: 1px solid #ddd;
  font-size: 26px;
  color: # 888;
  cursor: pointer;
}

.calc-operation td: active {
  background-color: #ddd;
}

.calc-operation .cls {
  color: # ee8956;
}
This static calculator is rough ~~

 

3. JS logic

This part is the focus, step by step

The first is to monitor the calculator, that is, this form, you can use the event delegation method to monitor and process on the parent node

    // Bind event
    bindEvent: function () {
      var that = this;

      that. $ operation.on ('click', function (e) {
        e = e || window.event;
        var elem = e.target || e.srcElement,
          val,
          action;

        if (elem.tagName === 'TD') {
          val = elem.getAttribute ('data-val') || elem.getAttribute ('data-ac');

Listening to the data, you only get a certain value / operator on the page, so you need to store the data to form an infix, then convert the infix to a suffix, and finally calculate by the suffix

    // infix expression
    this.infix = [];
    // Suffix expression
    this.suffix = [];
    // Suffix expression operation result set
    this.result = [];
Implemented according to the algorithm steps, no parentheses are used here, if necessary, you can modify the judgment conditions at the corresponding position ~

    // Infix expression to suffix
    infix2Suffix: function () {
      var temp = [];
      this.suffix = [];

      for (var i = 0; i <this.infix.length; i ++) {
        // numeric value, directly press in
        if (! this.isOp (this.infix [i])) {
          this.suffix.push (this.infix [i]);
        }
        else {
          if (! temp.length) {
            temp.push (this.infix [i]);
          }
          else {
            var opTop = temp [temp.length-1];
            // Loop to determine operator priority and push higher operator into suffix expression
            if (! this.priorHigher (opTop, this.infix [i])) {
              while (temp.length &&! this.priorHigher (opTop, this.infix [i])) {
                this.suffix.push (temp.pop ());
                opTop = temp [temp.length-1];
              }
            }
              // Push the current operator into the suffix expression
            temp.push (this.infix [i]);
          }
        }
      }
      // Push the remaining arithmetic symbols into
      while (temp.length) {
        this.suffix.push (temp.pop ());
      }
    },

 // Suffix expression calculation
    calcSuffix: function () {
      this.result = [];

      for (var i = 0; i <this.suffix.length; i ++) {
        // numeric value, directly pushed into the result set
        if (! this.isOp (this.suffix [i])) {
          this.result.push (this.suffix [i]);
        }
        // Operator, take two items from the result set for operation, and put the result into the result set
        else {
          this.result.push (this.opCalc (this.result.pop (), this.suffix [i], this.result.pop ()));
        }
      }
      // At this time there is only one value in the result set, which is the result
       return this.result [0];
    }

In fact, when implementing it, you will find that the infix and suffix are just a difficulty. The more complicated thing is the state change (or data change) of the entire calculator

In this simple calculator, there are data and operations such as numbers (0-9), operators (+-* /), operations (clear and delete), pre-operations (percent sign squared), decimal point, and immediate operations

If it is a scientific calculator, it is more complicated, so it is critical to sort out how to control these things, and the most important one is the construction and storage of infix expressions.

 When the + sign is clicked continuously, it does not conform to the actual operation, so a variable lastVal is needed to record the last value, which is updated with the operation, and then judged to prevent the program from making mistakes

After clicking the = sign, we can continue to use this result for calculation, or restart the calculation

// Build infix expression
    buildInfix: function (val, type) {
      // After the direct click equal operation,
      if (this.calcDone) {
        this.calcDone = false;
        // Click on the number again to start a new calculation
        if (! this.isOp (val)) {
          this.resetData ();
        }
        // Click on the operator again to continue the operation using the current result value
        else {
          var re = this.result [0];
          this.resetData ();
          this.infix.push (re);
        }

      }

      var newVal;
       ...

Click delete, delete Except for one digit, instead of deleting a number directly, then update the value of the infix expression

      // delete operation
      if (type === 'del') {
        newVal = this.infix.pop ();
        // delete the last digit
        newVal = Math.floor (newVal / 10);
        if (newVal) {
          this.infix.push (newVal);
        }

        this.lastVal = this.infix [this.infix.length-1];
        return this.infix;
      }

The addition operation needs to be considered more, such as continuous continuous operators, continuous numbers, operator +-connected with numbers to indicate positive and negative numbers, connection access to the decimal point, etc.

      // Add operation, first determine whether the operator is repeated
      else if (type === 'add') {
        // Two consecutive operators
        if (this.isOp (val) && this.isOp (this.lastVal)) {
          return this.infix;
        }
        // two consecutive numbers
        else if (! this.isOp (val) &&! this.isOp (this.lastVal)) {
          newVal = this.lastVal * 10 + val;
          this.infix.pop ();
          this.infix.push (this.lastVal = newVal);

          return this.infix;
        }
        // The first number is positive or negative
        if (! this.isOp (val) && this.infix.length === 1 && (this.lastVal === '+' || this.lastVal === '-')) {
          newVal = this.lastVal === '+'? val: 0-val;
          this.infix.pop ();
          this.infix.push (this.lastVal = newVal);

          return this.infix;
        }


        this.infix.push (this.lastVal = val);
        return this.infix;
      }

In many operations, the calculator needs to perform calculations in real time. To simplify the code, it can be packaged into a method and called at the corresponding position.

    // immediate calculation
    calculate: function (type) {
      this.infix2Suffix ();
      var suffixRe = this.calcSuffix ();

      if (suffixRe) {
        this. $ out.text ('=' + suffixRe)
          .attr ('title', suffixRe)
          .removeClass ('active');

        // If the equality operation is performed directly
        if (type === 'eq') {
          this. $ in.removeClass ('active');
          this. $ out.addClass ('active');
          // Set the mark: the calculation is currently displayed
          this.calcDone = true;
          this.lastVal = suffixRe;
          // Set history
          var history = this.infix.join ('') + '=' + suffixRe;
          this. $ history.text (history) .attr ('title', history);
        }

      }
    },

The rest is the processing after clicking, which is a variety of call processing. Passing data-> building infix processing data-> infix to suffix-> suffix calculation

For example, click the number

    // Number: 0-9
          if (! isNaN (parseInt (val, 10))) {
            // Build infix expression and display
            var infixRe = that.buildInfix (parseInt (val, 10), 'add');
            that. $ in.text (infixRe.join ('')). addClass ('active');

            that.calculate ();

            return;
          }

Another example is a few pre-calculations, which are actually similar.

       // Pre-calculation: percentage, decimal point, square
          else if (['per', 'dot', 'sq']. indexOf (action)! == -1) {
            if (! that.infix.length || that.isOp (that.lastVal)) {
              return;
            }

            if (action === 'per') {
              that.lastVal / = 100;
            } else if (action === 'sq') {
              that.lastVal * = that.lastVal;
            } else if (action === 'dot') {
              // that.curDot = true;
            }

            // Rebuild the infix expression
            var infixRe = that.buildInfix (that.lastVal, 'change');
            that. $ in.text (infixRe.join ('')). addClass ('active');

            that.calculate ();
          }

The above is the implementation steps of this simple calculator. There are too many changes and I ca n’t guarantee that I wo n’t make mistakes.

The basic logic is the same. If you want to add the functions of scientific calculators such as decimal point operation, bracket operation, sine and cosine, you should implement it yourself. . Big brain. .

$ (function () {

  function Calculator ($ dom) {
    this. $ dom = $ ($ dom);
    // Historical operations
    this. $ history = this. $ dom.find ('. calc-history');
    // input area
    this. $ in = this. $ dom.find ('. calc-in');
    // output area
    this. $ out = this. $ dom.find ('. calc-out');
    this. $ operation = this. $ dom.find ('. calc-operation');

    // Operator mapping
    this.op = {
      'plus': '+',
      'minus': '-',
      'mul': '*',
      'div': '/'
    };
    this.opArr = ['+', '-', '*', '/'];

    // infix expression
    this.infix = [];
    // Suffix expression
    this.suffix = [];
    // Suffix expression operation result set
    this.result = [];
    // store the most recent value
    this.lastVal = 0;
    // The current calculation is equal to completion
    this.calcDone = false;
    // The decimal point (.) Related value is currently being corrected
    this.curDot = false;

    this.init ();
  }

  Calculator.prototype = {
    constructor: Calculator,
    // initialize
    init: function () {
      this.bindEvent ();
    },
    // Bind event
    bindEvent: function () {
      var that = this;

      that. $ operation.on ('click', function (e) {
        e = e || window.event;
        var elem = e.target || e.srcElement,
          val,
          action;

        if (elem.tagName === 'TD') {
          val = elem.getAttribute ('data-val') || elem.getAttribute ('data-ac');
          // Number: 0-9
          if (! isNaN (parseInt (val, 10))) {
            // Build infix expression and display
            var infixRe = that.buildInfix (parseInt (val, 10), 'add');
            that. $ in.text (infixRe.join ('')). addClass ('active');

            that.calculate ();

            return;
          }

          action = val;

          // Operation: clear, delete, calculate equal to
          if (['cls', 'del', 'eq']. indexOf (action)! == -1) {
            if (! that.infix.length) {
              return;
            }

            // Clear data
            if (action === 'cls' || (action === 'del' && that.calcDone)) {
              that. $ in.text ('');
              that. $ out.text ('');

              that.resetData ();
            }
            // clear
            else if (action === 'del') {
              // Rebuild the infix expression
              var infixRe = that.buildInfix (that.op [action], 'del');
              that. $ in.text (infixRe.join ('')). addClass ('active');

              that.calculate ();

            }
            // equals
            else if (action === 'eq') {
              that.calculate ('eq');

            }
          }
          // Pre-calculation: percentage, decimal point, square
          else if (['per', 'dot', 'sq']. indexOf (action)! == -1) {
            if (! that.infix.length || that.isOp (that.lastVal)) {
              return;
            }

            if (action === 'per') {
              that.lastVal / = 100;
            } else if (action === 'sq') {
              that.lastVal * = that.lastVal;
            } else if (action === 'dot') {
              // that.curDot = true;
            }

            // Rebuild the infix expression
            var infixRe = that.buildInfix (that.lastVal, 'change');
            that. $ in.text (infixRe.join ('')). addClass ('active');

            that.calculate ();
          }
          // Operator: +-* /
          else if (that.isOp (that.op [action])) {
            if (! that.infix.length && (that.op [action] === '*' || that.op [action] === '/')) {
              return;
            }

            var infixRe = that.buildInfix (that.op [action], 'add');
            that. $ in.text (infixRe.join ('')). addClass ('active');
          }
        }
      });
    },

    resetData: function () {
      this.infix = [];
      this.suffix = [];
      this.result = [];
      this.lastVal = 0;
      this.curDot = false;
    },

    // Build infix expression
    buildInfix: function (val, type) {
      // After the direct click equal operation,
      if (this.calcDone) {
        this.calcDone = false;
        // Click on the number again to start a new calculation
        if (! this.isOp (val)) {
          this.resetData ();
        }
        // Click on the operator again to continue the operation using the current result value
        else {
          var re = this.result [0];
          this.resetData ();
          this.infix.push (re);
        }

      }

      var newVal;

      // delete operation
      if (type === 'del') {
        newVal = this.infix.pop ();
        // delete the last digit
        newVal = Math.floor (newVal / 10);
        if (newVal) {
          this.infix.push (newVal);
        }

        this.lastVal = this.infix [this.infix.length-1];
        return this.infix;
      }
      // Add operation, first determine whether the operator is repeated
      else if (type === 'add') {
        // Two consecutive operators
        if (this.isOp (val) && this.isOp (this.lastVal)) {
          return this.infix;
        }
        // two consecutive numbers
        else if (! this.isOp (val) &&! this.isOp (this.lastVal)) {
          newVal = this.lastVal * 10 + val;
          this.infix.pop ();
          this.infix.push (this.lastVal = newVal);

          return this.infix;
        }
        // The first number is positive or negative
        if (! this.isOp (val) && this.infix.length === 1 && (this.lastVal === '+' || this.lastVal === '-')) {
          newVal = this.lastVal === '+'? val: 0-val;
          this.infix.pop ();
          this.infix.push (this.lastVal = newVal);

          return this.infix;
        }

      // TODO: decimal point operation
      // if (this.isOp (val)) {
      // this.curDot = false;
      //}

      // // Decimal point
      // if (this.curDot) {
      // var dotLen = 0;
      // newVal = this.infix.pop ();
      // dotLen = newVal.toString (). split ('.');
      // dotLen = dotLen [1]? dotLen [1] .length: 0;

      // newVal + = val / Math.pow (10, dotLen + 1);
      // // Correct the exact value of decimal point operation
      // newVal = parseFloat (newVal.toFixed (dotLen + 1));

      // this.infix.push (this.lastVal = newVal);
      // return this.infix;
      //}

        this.infix.push (this.lastVal = val);
        return this.infix;
      }

      // Change operations, such as% pre-calculation
      else if (type === 'change') {
        this.infix.pop ();
        this.infix.push (this.lastVal = val);

        return this.infix;
      }

    },
    // determine if it is an operator
    isOp: function (op) {
      return op && this.opArr.indexOf (op)! == -1;
    },
    // determine operator priority
    priorHigher: function (a, b) {
      return (a === '+' || a === '-') && (b === '*' || b === '/');
    },
    // Perform operator operations
    opCalc: function (b, op, a) {
      return op === '+'
        ? a + b
        : op === '-'
        ? a-b
        : op === '*'
        ? a * b
        : op === '/'
        ? a / b
        : 0;
    },
    // immediate calculation
    calculate: function (type) {
      this.infix2Suffix ();
      var suffixRe = this.calcSuffix ();

      if (suffixRe) {
        this. $ out.text ('=' + suffixRe)
          .attr ('title', suffixRe)
          .removeClass ('active');

        // If the equality operation is performed directly
        if (type === 'eq') {
          this. $ in.removeClass ('active');
          this. $ out.addClass ('active');
          // Set the mark: the calculation is currently displayed
          this.calcDone = true;
          this.lastVal = suffixRe;
          // Set history
          var history = this.infix.join ('') + '=' + suffixRe;
          this. $ history.text (history) .attr ('title', history);
        }

      }
    },

    // Infix expression to suffix
    infix2Suffix: function () {
      var temp = [];
      this.suffix = [];

      for (var i = 0; i <this.infix.length; i ++) {
        // numeric value, directly press in
        if (! this.isOp (this.infix [i])) {
this.suffix.push (this.infix [i]);
        }
        else {
          if (! temp.length) {
            temp.push (this.infix [i]);
          }
          else {
            var opTop = temp [temp.length-1];
            // Loop to determine operator priority and push higher operator into suffix expression
            if (! this.priorHigher (opTop, this.infix [i])) {
              while (temp.length &&! this.priorHigher (opTop, this.infix [i])) {
                this.suffix.push (temp.pop ());
                opTop = temp [temp.length-1];
              }
            }
              // Push the current operator into the suffix expression
            temp.push (this.infix [i]);
          }
        }
      }
      // Push the remaining arithmetic symbols into
      while (temp.length) {
        this.suffix.push (temp.pop ());
      }
    },

    // Suffix expression calculation
    calcSuffix: function () {
      this.result = [];

      for (var i = 0; i <this.suffix.length; i ++) {
        // numeric value, directly pushed into the result set
        if (! this.isOp (this.suffix [i])) {
          this.result.push (this.suffix [i]);
        }
        // Operator, take two items from the result set for operation, and put the result into the result set
        else {
          this.result.push (this.opCalc (this.result.pop (), this.suffix [i], this.result.pop ()));
        }
      }
      // At this time there is only one value in the result set, which is the result
       return this.result [0];
    }
  };

  new Calculator ('. calc-wrap');
});
The above is the whole content of this article, I hope it will be helpful to everyone's learning, and I hope everyone will support the home of helpers.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.