1

I’m working on a Calculator app. I'm collecting the numbers (1,2,3…) and operators (+,-,*,/, =) as parameters from html onclick method and using eval() in JavaScript to compute the expression. Something like this..

<button type="submit" class="button-style" onclick="myfunc('7')" id="seven"> 7 </button>    
<button type="submit" class="button-style" onclick="myfunc('8')" id="eight"> 8 </button>    
<button type="submit" class="button-style" onclick="myfunc('9')" id="nine"> 9 </button>

My JavaScript file:-

function myfunc(para) {
  if (para != "=" && para != "AC" && para != "ERS" && para != "+/-") {
    document.getElementById("user-input").innerHTML += para;
    document.getElementById("user-input").style.display = "block";
  } else if (para === "ERS") {
    document.getElementById("user-input").innerHTML = document
      .getElementById("user-input")
      .innerHTML.slice(0, -1);
  } else if (para === "AC") {
    document.getElementById("user-input").innerHTML = " ";
    document.getElementById("result").innerHTML = " ";
  } else if (para === "+/-") {
    let newvar = document.getElementById("user-input").innerHTML;
    let anothervar = newvar * -1;
    document.getElementById("user-input").innerHTML = anothervar;
  } else {
    let evaldata = document.getElementById("user-input").innerHTML;
    let newevaldata = evaldata.replace(/^0+/, ""); //Omitting the leading zeroes
    let resultData = eval(newevaldata);
    let num = resultData.toFixed(2);
    document.getElementById("result").innerHTML = num;
  }
}

And this is how the calculator looks like:- Calculator

My question:

Everything is working fine except one strange issue.
Whenever I try to add 010+100 it shows the result as 108
or if I try 0000010+1000 the result is 1008, or 0023+3200 gives 3219.

How is that possible and how to omit the preceding zeroes, I have tried with a regular expression but it doesn't work. Without having preceding zeros the app works just fine. I am stuck here. Please help me out. Thanks.

Here is a screenshot:- calculation error

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
A K
  • 11
  • 2
  • 3
    Leading 0s are treated as octal – Daniel A. White Sep 25 '22 at 22:28
  • Okay I understand, but how can I omit those zeroes while using eval(). I'm a newcomer. Tysm for your input. – A K Sep 25 '22 at 22:32
  • You can remove leading zeros when adding numbers to the string. – Konrad Sep 25 '22 at 22:34
  • In this case the whole string is like a expression (e.g 0010+1100+1234), so I'm not so sure how to remove those very first zeroes from this string, or they might appear randomly anywhere else in the string. So I'm not sure how to tell the computer to discard any zeroes before any number. That's where I am stuck. Let me know. Thanks a lot. – A K Sep 25 '22 at 22:39
  • @jabaa It just might. – A K Sep 25 '22 at 22:41
  • `eval` evaluates a string as JavaScript code. This is the expected behavior of JavaScript. You have to either parse the string or modify it. I prefer to parse the string and completely avoid `eval`, because it can be dangerous in many cases. – jabaa Sep 25 '22 at 22:42
  • What is `**let` in your code? – Roko C. Buljan Sep 25 '22 at 22:49
  • Also, avoid the use of `eval`, specially from a user input. Instead use `new Function` as described in this answer: https://stackoverflow.com/a/66763441/383904 – Roko C. Buljan Sep 25 '22 at 22:50
  • Also, avoid using inline JS `on*` like `onclick` handlers (just like i.e: inline `style`). JS should be in one place only, and that's the respective ` – Roko C. Buljan Sep 25 '22 at 22:52
  • The ** happened when I tried to make the text "bold" from this editor here in stackoverflow. – A K Sep 25 '22 at 22:57
  • I understand everything you've told me, I will try to avoid using eval() . – A K Sep 25 '22 at 22:58

1 Answers1

2

Remove leading zeroes from numbers in a mathematical operation using this helper function:

const cleanNum = (str) => str.replace(/\d*(\.\d+)?/g, n => n && +n);

Example:

const cleanNum = (str) => str.replace(/\d*(\.\d+)?/g, n => n && +n);

const userStr = "1e2 - 0011 + 0.1100 * 00.0001 -0+ -0 / (001 + 010 + 000) - 00100";
const clean = cleanNum(userStr); // 1e2 - 11 + 0.11 * 0.0001 -0+ -0 / (1 + 10 + 0) - 100

console.log(clean);

const result = new Function(`return (${clean})`)();

console.log(result); // -10.999989

And now some additional info:

Never use eval. It's considered evil specially from a user-input — since it evaluates inside the browser any user-submitted value.

Use new Function instead.

Although (as mistakenly suggested) being an issue of parseInt() — it's not.
parseInt("010") will return the correctly expected 1010 (base 10) in all modern browsers. There's a deprecation of 0-prefixed octal literals in strict-mode which can be seen at work by specifically using parseInt, as seen.

The issue is strictly with the 010 Octal representation due to the leading 0 — in "sloppy mode"

console.log(010) // 8

You could adhere to a more battle-proof syntax by using Strict Mode (or by using module type)

"use strict"
console.log(010) // Uncaught SyntaxError: Octal literals are not allowed in strict mode

eval("010 + 100") evaluates the first operand 010 as octal (value 8 in decimal), resulting in 8 + 100. Just like doing i.e: parseInt("011", 8) === 9; // true by using intentionally the radix 8.

Even if suggested to use new Function - the issue still remains true

"use strict";
console.log(new Function("return (010 + 100);")()) // 108 

therefore: it's your job as a developer to:
get rid of any leading "0" from your String before passing them to new Function.


Finally, back to your specific code:

  • Use new Function instead of evil (OK, you got that one by now)
  • <button type="submit" should be <button type="button"
  • Don't use HTML-inline on* handlers like onclick="myfunc('1')". JS should be in one place only, and that's the respective <script> tag or file. Use Element.addEventListener() instead.
  • Don't use document.getElementById("user-input").innerHTML from a contenteditable element. Contenteditable is a total browser mess and we're still waiting for a better API to emerge - and it's an unreliable. Use document.getElementById("user-input").value instead, from an <input> Element.
  • Cache your elements you're planning to reuse beforehand. Querying the DOM on every button click might result in a poor user experience (and a laggy calculator).
  • Don't use IDs in HTML, JS. Imagine you want many calculators (as components) in a single page; by using IDs in multiple places could raise unwanted bugs singe ID is supposed to be unique.
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • 1
    Thanks a lot for the detailed explanation. Appreciate that. – A K Sep 26 '22 at 05:03
  • To be very honest this is a very high level implementation, I don’t really understand what’s really going on here. I have a long way to get there probably. I’m just looking for a vanilla JavaScript code that will omit the leading zeroes from any equation. Example: (0010 + 123 + 0123) should get converted to (10 + 123 + 123). Thanks. – A K Sep 26 '22 at 11:42
  • As I pointed out in bold, pick any calculator. The one from your phone i.e. Now, try to enter many consecutive 000. As you can see, you're not allowed to. So, as stated above. It's your job to prevent the user to enter such numbers format. I think that's the easiest solution. – Roko C. Buljan Sep 26 '22 at 13:11
  • Yeah I can see that your version of the calculator is not accepting leading zeros. I just don’t understand what you did to make it like that. I’m really unaware of advanced concepts. Anyways thanks a lot for the code. I’ll figure something out. Thanks for your time. – A K Sep 26 '22 at 13:52
  • @AK. Edited my answer. Added a small function `cleanNum` that should help you achieve the desired. – Roko C. Buljan Sep 27 '22 at 12:53
  • Thanks a lot. This will do. I really appreciate it. I had no clue what to do and I was stuck. Thanks again. – A K Sep 28 '22 at 08:06
  • Just amazing. It is now working as I wanted it to. Thanks a lot again. Please check out the calculator here:- https://abhishekk119.github.io/calculator/ (only for desktop) It also accepts input from keyboard. Escape key is AC. – A K Sep 28 '22 at 08:28
  • @ak I'm sad to see you still use `type="submit"` on your buttons... `event.preventDefault();` inside every `if` is not needed. only one at the top. Also, you're still using `eval` instead of `new Function`. etc etc. – Roko C. Buljan Sep 28 '22 at 09:03
  • 1
    I will fix all that. And I’ll update the final version here shortly. You really motivate me to learn more, I’m trying to land a job within 6 to 8 months as a front end developer. I still need to learn a lot of things. Thanks again for your help. – A K Sep 28 '22 at 09:14