Calculator issues, 2 ½ hours spent and can't find the problem

–> https://codepen.io/Mike-was-here123/pen/QrdJdO?editors=0010

You will notice how when use the Geometry section, it works fine. Only one output in the output log. Now if you go and do the PT section, you also only get 1 output (i know the output is messed up there w/ the test, will fix).

Now after that if you go back and do the Geometry section again, you get two duplicated outputs, same with PT.

Here is the code for a visual on whats happening (213+)

else if (id === "GEOM") {
      memoryExtra = "GEOM"
      notEquations = 1;
      $("#log").html("");
  
      $("#equal").on("click", function() {
        answer = 0;
            if (memoryExtra === "GEOM") {
        var shape = $("#selectorGEOM :selected").text();
        if (shape === "H.S.") {
          shape = "hemisphere";
        }
        var find = $("#findGEOM :selected").text();
        var part1 = $("#1GEOM").val();
        var part2 = $("#2GEOM").val();
        var answer = geo(shape, find, part1, part2);
           console.log(answer)
        $("#log").append("<div class='outputElement'><ul><li><div class='row'><div class='xOutput'>"+answer[0]+"</div><div class='yOutput'>"+answer[1]+"</div></div></li></ul></div>")
            }       
      });
      
    
      
    } else if (id === "PT") {
      memoryExtra = "PT"
      console.log(memoryExtra)
      notEquations = 1
      $("#output").html("");
      $("#log").html("");
      );
      $("#equal").on("click", function() {
         if (memoryExtra === "PT") {
         answer = 0;
        answerPT = 0;
        answerPT =
          $("#firstSelector :selected").text() === "Right"
            ? pt($("#1PT").val(), $("#2").val(), $("#3").val())
            : pt(
                $("#1PT").val(),
                $("#2PT").val(),
                $("#selectorPT :selected").text()
              );
    
         $("#log").append("<div class='outputElement'><ul><li><div class='row'><div class='xOutput'>"+answerPT[0]+"</div><div class='yOutput'>"+answerPT[1]+"</div></div></li></ul></div>")
         }
      });  
   

I cut out a lot of unimportant stuff, so you could see how it works. Look carefully (more recommend the Codepen one) and you can see its just simple jquery and calling functions. It also isn’t the functions either (top of the code) that are causing this error.

This also happens with all the if statements, all will duplicate for however many times you do it. Just read over the code on the codepen, its fairly simple but lengthy.

The first issue i have, is that when i go to press the equal button, all the equal on click functions happen inside of all the if statements, even though i check for ID. I have no idea why this happens.

To try and get around this, i did memoryExtra, which is changed when a if statement is called. This makes it so even though all the equal functions are activated and, only one can do anything.

$("#equal").on("click", function() {
        answer = 0;
            if (memoryExtra === "GEOM") {
// here. This could be false when im on PT.
        var shape = $("#selectorGEOM :selected").text();
        if (shape === "H.S.") {
          shape = "hemisphere";
        }
        var find = $("#findGEOM :selected").text();
        var part1 = $("#1GEOM").val();
        var part2 = $("#2GEOM").val();
        var answer = geo(shape, find, part1, part2);
           console.log(answer)
        $("#log").append("<div class='outputElement'><ul><li><div class='row'><div class='xOutput'>"+answer[0]+"</div><div class='yOutput'>"+answer[1]+"</div></div></li></ul></div>")
            }       
      });

This appears to work, since i get no errors in the console.

This is where i’m to my issue, why does it duplicate in the output?

var answer = geo(shape, find, part1, part2);
console.log(answer)
 $("#log").append("<div class='outputElement'><ul><li><div class='row'><div class='xOutput'>"+answer[0]+"</div><div class='yOutput'>"+answer[1]+"</div></div></li></ul></div>")

This is what i use to push onto the output (#log). This is all i’m doing. No loops that would cause this to happen twice, even more for how many times you do it.

This is my best guess after playing around for a bit (I’m quite confident that it’s the case though!): I think it’s because you have jQuery('#equal').on('click', callback) inside the if ... else statements for every one of the “special” types of calculations.

Every time you click on “GEOM.”, “PT”, “Prime Number”, “Algebra” or “Combinations”, the corresponding piece of code gets triggered and adds an event listener of that type to #equal. You can see this in dev tools:

  1. Right click on the = button and select “Inspect (Element)”
  2. Double check that the element highlighted in the Inspector/Element tab is #equal
  3. Click on the GEOM. button
  4. Check the number of event listeners attached to the element:
    • In Firefox Developer Edition, an “event” bubble is appended to the element in the Inspector; click on it and note the number of event listeners in there.
    • In Chrome, select the “Event Listeners” tab for the element, uncheck “Ancestors”, and expand “click”. Note that (at least with my version this is the case) you may have to select another element and then reselect #equal for this to update
  5. Repeat Step 3 and Step 4 to see multiple event listeners being created

For future reference, and if you’re not already using a code editor, I think it’s a good idea to get used to it for anything that’s more than a hundred or so lines. Spending a few days to set up, configure, and get used writing modularised code will save you a lot of time spent on debugging.

I hope that helps! :smile:

What would be a way to go about solving this?

Simply combine the logic in the separate jQuery.('#equal)' blocks of code into a singlejQuery(’#equal)’` and makes sure that it is only ever triggered once (be added to the equal button)!

I’m confused, do you have a example?

Ignoring the variable assignments and the single, gigantic function definition (solve), your code current starts here:

document
  .getElementById("buttonRows")
  .addEventListener("click", e => solve(e.target.id));

To reiterate what was said in the previous posts is that you should not be calling $('#equal').on('click', callback) inside solve() because it adds an event listener to #equal whenever it’s triggered. In this particular case, you should only have one event listener on '#equal.

Since you are keeping track of what mode the calculator is current in with memoryExtra, one way to do what you want is something like this (not tested):

const memoryExtra = [];
// By the way, why is memoryExtra initialised with an empty array?
// It looks like it's only ever a string on number.

function equalHandler() {
  switch (memoryExtra) {
    case "GEOM":
      // Logic extracted from solve()
      var answer = 0;
      var shape = $("#selectorGEOM :selected").text();
      if (shape === "H.S.") {
        shape = "hemisphere";
      }
      var find = $("#findGEOM :selected").text();
      var part1 = $("#1GEOM").val();
      var part2 = $("#2GEOM").val();
      var answer = geo(shape, find, part1, part2);
      $("#log").append(
        "<div class='outputElement'><ul><li><div class='row'><div class='xOutput'>" +
          answer[0] +
          "</div><div class='yOutput'>" +
          answer[1] +
          "</div></div></li></ul></div>"
      );
      break;

    case "PT":
      // ... etc.

    default:
      console.log("Something went horribly wrong.");
      break;
  }
}

document
  .getElementById('buttonRows')
  .addEventListener("click", e => solve(e.target.id));

document
  .getElementById('equal')
  .addEventListener("click", equalHandler);

I suggest taking some time to understand the problem, play with things on a separate, smaller Pen and understand the cause first. Otherwise it’ll just come up again in the future and more time will wasted on just getting things to work.

Sorry i’m late to respond, personal things.

–> https://codepen.io/Mike-was-here123/pen/QrdJdO?editors=1010

Now when i click on GEOM, the same duplicate things happens

On line 219 i still have that solve function, and on line 221 i change the value of memoryExtra. On line 225 i check if the ID is not GEOM (for example). Then on line 326 i do the else for that, and call equal handler function which is located at the top of the code.

I get duplicates when i do the GEOM function, and i come across the error (console) for PT because its trying to do all the equal functions and it doesn’t work for GEOM. I have a way around this, but this would again result in duplicates.

document
  .getElementById('equal')
  .addEventListener("click", equalHandler);

code seems to be doing nothing. I am trying to do what you are saying, but its not working. How can i integrate this, with the solve function, and the equal button, and not get duplicates or errors.

I had a quick look and it looks like you haven’t decoupled the logic for mode selection and equal at all—notice that the code in my previous snippet is only meant to handle when = is pressed and for the special cases; it’s there to help you to reason about part of your code and not there as a solution for you (that’s your job, not my job). In your current code equalHandler() actually handles both when GEOM, PT... etc. and when equal is pressed, which is not what I suggested in the previous post at all.

In addition, you are still calling $('#equal').on("click", callback); every time the GEOM button (or any other special mode buttons, I’ll just abbreviate it as GEOM now) is pressed, so it’s only natural that you’re still getting duplicated outputs. This is the issue that I pointed out in the very first response—you should take some time to think about the sequence of execution and make sure that doesn’t happen.

The additional duplication comes from the fact that each time you press = you are triggering both of these:

  • document.getElementById("equal").addEventListener("click", equalHandler)
  • The else { equalHandler(); } block inside the solve() function.

Since you are still creating event listeners inside equalHandler() and it gets called twice, it’s only natural that you are doubling the problem from before.

Fixing any of this is meaningless if you don’t spend the time to reason about your code and understand the problem. Duct taping solutions is also a waste of everyone’s time because something else will just break.

I have already said everything that needs to be said about the problem, how to address it, and how to go about it in the future to avoid this from happening. If it’s a conceptual issue around what I said then I’m more than happy to expand on it, otherwise the following is the last thing I will say on this and hopefully it will bring you towards the right direction:

  • Rename equalHandler on line 3 and line 332 assomeFunction

  • On line 331, create a new function called equalHandler

    function equalHandler() {
    
    }
    
  • Comment out line 17 to line 34

  • Copy line 18 to line 33 to inside the new equalHandler() and uncomment them

    function equalHandler() {
      answer = 0;
      var shape = $("#selectorGEOM :selected").text();
      if (shape === "H.S.") {
        shape = "hemisphere";
      }
      var find = $("#findGEOM :selected").text();
      var part1 = $("#1GEOM").val();
      var part2 = $("#2GEOM").val();
      var answer = geo(shape, find, part1, part2);
      $("#log").append(
        "<div class='outputElement'><ul><li><div class='row'><div class='xOutput'>" +
        answer[0] +
        "</div><div class='yOutput'>" +
        answer[1] +
        "</div></div></li></ul></div>"
      );
    }
    

Test the GEOM mode and, the results will be correctly displayed but it’s still duplicating. Now the duplication is not because you are adding multiple event handlers anymore—you can verify this by switching modes and it’ll still only add one entry every time you press equal (instead of 1, 2, 3, 4… etc. like before). This is a bug that has always been there in your code—you are just appending elements to #log and not cleaning anything up. So following from above:

  • Add $('.outputElement').remove(); to line 341

The duplication should now be gone and you should be getting the intended behaviour for the GEOM mode. Obviously the code above only fixes the GEOM mode and the problem is still present in the other modes, I was under the impression that you are using memoryExtra to keep track of the current mode, but it seems that it either doesn’t do that anymore or it never has. Just so there is no ambiguity in how to go about the rest of it:

  • Add var currentMode = ''; to line 2
  • Add currentMode = 'GEOM'; to line 8
  • Add switch (currentMode) { to line 334 and case 'GEOM': to line 335
  • Close the switch by adding } to line 353

Test that everything is still working, and it should provided that I haven’t mistyped anything. This is the basic idea of how you would keep track of different modes and you can now do the migration for the other modes (from someFunction to equalHandler).

These changes are purely there to guide you to realise what the problem is and the outcome is by no means how I personally would approach it and/or considered good design.

In case I’ve made a mistake, the code is appended. I hope that helps!

$("document").ready(function() {
  var currentMode = "";
  var memoryExtra = 0;
  function someFunction() {
    // I just define the function, is not actually caled yet.
    switch (memoryExtra) {
      case "GEOM":
        currentMode = "GEOM";
        notEquations = 1;
        $("#output").html("");
        $("#output").append(
          "<div class='row' id='marginGeom'><select class='selector' id='selectorGEOM'><option value='option1'>Cylinder</option><option value='option2'>Cone</option><option value='option3'>Sphere</option><option value='option4'>H.S.</option></select><select class='selector' id='findGEOM'><option value='option1'>Volume</option><option value='option2'>Height</option><option value='option3'>Radius</option></select><input id='1GEOM' style='text-align: center;' placeholder='Part 1'></input><input id='2GEOM' placeholder='Part 2' style='text-align: center;'></input></div>"
        );
        $("#log").html("");
        $("#log").append(
          "<h2 class='helpG' style='text-align: center;text-decoration: underline;'>Selected Shape </h2><h2 class='helpG' style='text-decoration: underline;'>Volume:</h2><h2 class='helpG'> &nbsp; Radius, Height </h2><h2 class='helpG' style='text-decoration: underline;'>Height:</h2><h2 class='helpG'> &nbsp; Volume, Radius </h2><h2 class='helpG' style='text-decoration: underline;'>Radius:</h2><h2 class='helpG'> &nbsp; Volume, Height </h2><hr/><h2 class='helpOutputElement'>Click on box & use arrow keys</h2>"
        );

        // $("#equal").on("click", function() {
        // answer = 0;
        // var shape = $("#selectorGEOM :selected").text();
        // if (shape === "H.S.") {
        //   shape = "hemisphere";
        // }
        // var find = $("#findGEOM :selected").text();
        // var part1 = $("#1GEOM").val();
        // var part2 = $("#2GEOM").val();
        // var answer = geo(shape, find, part1, part2);
        // $("#log").append(
        //   "<div class='outputElement'><ul><li><div class='row'><div class='xOutput'>" +
        //     answer[0] +
        //     "</div><div class='yOutput'>" +
        //     answer[1] +
        //     "</div></div></li></ul></div>"
        // );
        // });

        break;
      case "PT":
        notEquations = 1;
        $("#output").html("");
        $("#log").html("");
        $("#output").append(
          "<div class='row' id='marginPT'><select class='selector' id='selectorPT'><option value='option1'>Right</option><option value='option2'>Leg</option><option value='option3'>C</option></select><input id='1PT'style='text-align: center;' placeholder='Part 1'></input><input id='2PT'style='text-align: center;' placeholder='Part 2'></input><input id='3PT'style='text-align: center;' placeholder='Part 3'></input></div>"
        );
        $("#log").html(
          "<h2 class='helpG' style='text-align: center;text-decoration: underline;'>Selected Finder</h2><h2 class='helpG' style='text-decoration: underline;'>Right:</h2><h2 class='helpG'> &nbsp; A, B, C </h2><h2 class='helpG' style='text-decoration: underline;'>Leg B:</h2><h2 class='helpG'> &nbsp; A, C </h2><h2 class='helpG' style='text-decoration: underline;'>Hypotenuse:</h2><h2 class='helpG'> &nbsp; A, B </h2><h2 class='helpWaver' style=''>&nbsp;&nbsp;This <b>only</b> works on right triangles &nbsp;&nbsp;or to find if they are right.</h2><hr/><h2 class='helpOutputElement'>Click on box & use arrow keys</h2>"
        );

        setInterval(function() {
          // unimportant to the issue
          $("#firstSelector :selected").text() === "Leg" ||
          $("#firstSelector :selected").text() === "C"
            ? $("#3PT").attr("disabled", "disabled")
            : $("#3PT").removeAttr("disabled", "disabled");
        }, 100);

        $("#equal").on("click", function() {
          if (memoryExtra === "PT") {
            answer = 0;
            answerPT = 0;
            answerPT =
              $("#firstSelector :selected").text() === "Right"
                ? pt($("#1PT").val(), $("#2").val(), $("#3").val())
                : pt(
                    $("#1PT").val(),
                    $("#2PT").val(),
                    $("#selectorPT :selected").text()
                  );

            $("#log").append(
              "<div class='outputElement'><ul><li><div class='row'><div class='xOutput'>" +
                answerPT[0] +
                "</div><div class='yOutput'>" +
                answerPT[1] +
                "</div></div></li></ul></div>"
            );
          }
        });
        break;
      case "prime":
        notEquations = 1;
        $("#log").html("");
        $("#output").html("");
        $("#output").append(
          "<div style='text-align: center;'><p>Input a number to extract its prime Numbers</p><input id='1PRIME' style='text-align: center;'class='prime' value='20000'/></div>"
        );

        $("#equal").on("click", function() {
          if (memoryExtra === "prime") {
            answer = [];
            var prime = sumPrimes($("#1PRIME").val());
            $("#log").append(
              " <div  style='padding-top: 16px;' class='primeOutputItem'>  <h3 class='primeOutputH3'> Prime count: " +
                prime[1] +
                "</h3> <hr/><h3 class='primeOutputH3'>" +
                prime[0] +
                "</h3></div><hr/>"
            );
          }
        });

        break;
      case "algebra":
        notEquations = 1;

        $("#output").html("");
        $("#output").append(
          "<div class='row'><h1 class='yAlge'>Y=</h1><input id='1Alge' class='algeInput'value='(x*3)**3'/><h1 class='xAlge'>X=</h1><input id='2Alge' class='algeX'value='1000'/></div>"
        );
        $("#log").html("");
        $("#equal").on("click", function() {
          if (memoryExtra === "algebra") {
            graph($("#1Alge").val(), $("#2Alge").val());
          }
        }); // equal
        break;
    } // switch
  } // function (equalHandler)

  var notEquations = 0;
  const pt = (input1, input2, input3) => {
    var arr = [input1, input2, input3];
    arr = arr.sort();
    return input3 === "C"
      ? ["C", Math.sqrt(input1 ** 2 + input2 ** 2)]
      : input3 === "Leg"
        ? ["B", Math.sqrt(input2 ** 2 - input1 ** 2)]
        : input1 ** 2 + input2 ** 2 === input3
          ? ["R", input1 ** 2 + " + " + input2 ** 2 + " === " + input3]
          : ["NR", input1 ** 2 + " + " + input2 ** 2 + " !== " + input3];
  };
  const sumPrimes = num => {
    var prime = [];
    var arr = [];
    for (let i = 2; i <= num; i++) {
      arr.push(i);
    }
    // create the list of numbers
    for (let i = 0; i < arr.length; i++) {
      var number = arr[i];
      // we know this number is prime
      prime.push(number + ",");
      for (var j = i + 1; j < arr.length; j++) {
        // so you dont count that same number again, second loop to go back through and take out any mutiples of selected number.
        if (arr[j] % number === 0) {
          // evenly devided?
          var arrayTail = arr.slice(j + 1);
          // last half of the array
          arr = arr.splice(0, j).concat(arrayTail);
          // first half, takes out that number and joins them together
        }
      }
      // after the loop is over, and than repeats again. Each time when you pick a number (line 10) we know its prime.
    }
    return [prime.join(" "), prime.length];
    // the prime numbers and how many there are.
  };

  const graph = (equation, range) => {
    var x = 0;
    while (x <= range) {
      console.log("when X=" + x + ", Y=" + eval(equation));
      x++;
    }
  };
  const geo = (shape, find, input1, input2) => {
    if (shape === "Cylinder") {
      return find === "Volume"
        ? ["V", Math.PI * Math.pow(input1, 2) * input2]
        : find === "Radius"
          ? ["R", Math.sqrt(input1 / (Math.PI * input2))]
          : find === "Height"
            ? ["H", +input1 / (Math.PI * Math.pow(input2, 2))]
            : null;
    }
    if (shape === "Cone") {
      return find === "Volume"
        ? ["V", Math.PI * Math.pow(input1, 2) * input2 / 3]
        : find === "Radius"
          ? ["R", Math.sqrt(input1 * 3 / (Math.PI * input2))]
          : find === "Height"
            ? ["H", input1 * 3 / (Math.PI * Math.pow(input2, 2))]
            : null;
    }
    if (shape === "Sphere") {
      return find === "Volume"
        ? ["V", 4 / 3 * (Math.PI * Math.pow(input1, 2))]
        : find === "Radius"
          ? ["R", Math.cbrt(input1 / Math.PI * (3 / 4))]
          : find === "Height"
            ? ["H", Math.cbrt(input1 / Math.PI * (3 / 4))]
            : null;
    }
    if (shape === "Hemisphere") {
      return find === "Volume"
        ? ["V", 2 / 3 * (Math.PI * Math.pow(input1, 2))]
        : find === "Radius"
          ? ["R", Math.cbrt(input1 / Math.PI * (3 / 4)) / 2]
          : find === "Height"
            ? ["H", Math.cbrt(input1 / Math.PI * (3 / 4)) / 2]
            : null;
    }
  };
  var answerPT = 0;
  var combination = 0;
  var algebraCheck = 0;
  var prime = 0;
  var geom = 0;
  var ptCheck = 0;
  var ans = 0; // So you can use your previous equation answer
  var answer = 0; // This is to check if you have awnsered, to give Ans a value
  var equation = [];
  var memory = [];
  var symbol = false;
  var showNumber = [];
  var number = false;
  const solve = id => {
    console.log(id);
    memoryExtra = id;
    if (id === "ac") {
      notEquations = 0;
    }
    if (
      id !== "GEOM" &&
      id !== "PT" &&
      id !== "prime" &&
      id !== "algebra" &&
      notEquations !== 1
    ) {
      $("#output").html("");
      $("#output").append("<h1 id='current'></h1><h1 id='equation'></h1>");
      //$("#log").html("")
      $("#current").html(equation);
      $("#equation").html(equation);
      symbol = false;
      number = false;
      if (id === "*" || id === "+" || id == "-" || id === "/") {
        showNumber = [];
        number = false;
        symbol = true;
      }
      if (
        isNaN(id) !== true ||
        id === "%" ||
        id === "(" ||
        id === ")" ||
        id === "ans" ||
        id === "√(" ||
        id === "∛(" ||
        id === "."
      ) {
        number = true;
        showNumber.push(id);
        $("#log").html("");
      }
      if (number === true && answer === 1) {
        // so it earases previous equation in case of it being a number when you go to type
        equation = [];
        equation.push(id);
        showNumber = [];
        $("#current").html(equation);
        $("#equation").html(equation);
        answer = 0;
        return (symbol = false);
      } else if (symbol === true && answer === 1) {
        // keeps previous equation if symbol and adds o that new equation ans, symbol (ans+)
        equation.push(ans);
        equation.push(id);
        $("#current").html(equation);
        $("#equation").html(equation);
        answer = 0;
        return (symbol = false);
      }
      if (id === "equal") {
        equation = equation.join("");
        memory.push(
          equation +
            " = " +
            eval(
              equation
                .replace("%", "/100")
                .replace("π", "Math.PI")
                .replace("√(", "Math.sqrt(")
                .replace("∛(", "Math.cbrt(")
            )
        );
        equation = eval(
          equation
            .replace("%", "/100")
            .replace("π", "Math.PI")
            .replace("√(", "Math.sqrt(")
            .replace("∛(", "Math.cbrt(")
        );
        ans = equation;
        $("#current").html(equation);
        $("#log").html("");
        equation = [];
        answer = 1;
      } else if (id === "ac") {
        memoryExtra = 0;
        notEquations = 0;
        $("#current").html("");
        $("#current").html("");
        ans = 0;
        equation = [];
        $("#output").html("");
        $("#output").append("<h1 id='current'></h1><h1 id='equation'></h1>");
        $("#log").html("");
        showNumber = [];
        symbol = false;
        number = false;
      } else {
        if (number === true) {
          $("#current").html(showNumber.join(""));
          // so you can see, for instance, 7863 at the same time
        } else {
          // or if teh previous thing was a symbol, erases that symbol and puts number and starts chain of numbers (line 169)
          $("#current").html(id);
        }
        equation.push(id);
        $("#equation").html(equation);
        // adds that number to teh equation and displays that.
      } // else for solve function
    } else {
      someFunction();
    }
  }; // solve (id)

  function equalHandler() {
    switch (currentMode) {
      case "GEOM":
        answer = 0;
        var shape = $("#selectorGEOM :selected").text();
        if (shape === "H.S.") {
          shape = "hemisphere";
        }
        var find = $("#findGEOM :selected").text();
        var part1 = $("#1GEOM").val();
        var part2 = $("#2GEOM").val();
        var answer = geo(shape, find, part1, part2);
        $(".outputElement").remove();
        $("#log").append(
          "<div class='outputElement'><ul><li><div class='row'><div class='xOutput'>" +
            answer[0] +
            "</div><div class='yOutput'>" +
            answer[1] +
            "</div></div></li></ul></div>"
        );
    }
  }
  document.getElementById("equal").addEventListener("click", equalHandler);
  document
    .getElementById("buttonRows")
    .addEventListener("click", e => solve(e.target.id));
});