0

Hello I am trying to create a script that will calculate a total by addition or subtraction. The issue I am running into is the current JavaScript I am using that I found only works with the Starting Amount field and only one of the Bill Amount fields at a time. For example if I input 50 in the starting amount and 50 into the Bill Amount field it will calculate it. If I add a row and then input an amount it will still only adjust the amount based on whats input into one of the Bill Amount fields.

Not sure why but the code does not function on jsfiddle but you will be able to see the code in this jsFiddle.

<!DOCTYPE html>
<html lang="en">
<head>
<title>Finance</title>
<meta charset="UTF-8">
<meta name="finance" content="width=device-width, initial-scale=1.0">
<style>


table {
padding-top: 15px;
}

td {
white-space: nowrap;
}

 button {
 cursor: pointer;
}

</style>
</head>   

<body>

<h1>Financial Keeps</h1>

<p><b>Starting Amount: &#36; <input type="number" id="startAmt"   name="startAmt" onkeyup="calc(this)"/></b></p>

<p>To subtract an amount place a minus (-) sign infront of the dollar amount.</p>

<button onclick="insertRow()" id="addRow" >Add Row</button>

<!--<button onclick="removeRow()" id="delRow" >Delete Row</button>-->

<table id="myTable">
<tr>
    <th>Bill Info</th>
    <th>Bill Amount</th>
    <th>Date</th>
    <th>Comment</th>
</tr>
<tr>
    <tr>
    <td><input type="text"    id="billInfo"></td>
    <td><input type="number"  id="billAmt" name="number"   onkeyup="calc(this)"></td>
    <td><input type="date"    id="date"></td>
    <td><input type="text"    id="commentBox"></td>
    <!--<td><input style="cursor: pointer;" type="button" id="delBtn"  value="Delete" onclick="removeRow(this)"></td>-->
</tr>
</table>

  <input type="hidden" id="total" name="total" value="0" />
  <p><b>Ending Amount: &#36; <span id="totalAmt">0</span></b></p>

  <script type="text/javascript">
function insertRow() {
var x = document.getElementById("myTable");
var row = x.insertRow(x.rows.length);

    var cell = row.insertCell(0);
    var a = document.createElement("input");
        a.setAttribute("type","text");
        cell.appendChild(a);

    var cell1 = row.insertCell(1);
    var b = document.createElement("input");
        b.setAttribute("type","number");
        b.setAttribute("id","billAmt");
        b.setAttribute("name","number");
        b.setAttribute("onkeyup","calc(this)");
        cell1.appendChild(b);

    var cell2 = row.insertCell(2);
    var c = document.createElement("input");
        c.setAttribute("type","date");
        cell2.appendChild(c);

    var cell3 = row.insertCell(3);
    var d = document.createElement("input");
        d.setAttribute("type","text");
        cell3.appendChild(d);

    var cell4 = row.insertCell(4);
    var e = document.createElement("button");
        e.innerText = "Delete";
        e.setAttribute("id","delBtn");
        e.setAttribute("onclick","removeRow(this)");
        cell4.appendChild(e); 
}

function removeRow(elem) {
var table = elem.parentNode.parentNode.parentNode;
var rowCount = table.rows.length;
var row = elem.parentNode.parentNode; 
row.parentNode.removeChild(row); 
}

var x = 0;
var y = 0;
var z = 0;
function calc(obj) {
var e = obj.id.toString();
if (e == 'startAmt') {
x = Number(obj.value);
y = Number(document.getElementById('billAmt').value);
} else {
    x = Number(document.getElementById('startAmt').value);
    y = Number(obj.value);
    }
    z = x + y;
    document.getElementById('total').value = z;
     document.getElementById('totalAmt').innerHTML = z;
}
</script>
</body>
</html>

The result I get:

enter image description here

Alfabravo
  • 7,493
  • 6
  • 46
  • 82
Mark White
  • 173
  • 2
  • 3
  • 14
  • _“Not sure why but the code does not function on JSFiddle”_ — [because you need to set the wrap method to ``](http://stackoverflow.com/q/7043649/4642212). – Sebastian Simon Mar 13 '17 at 18:16
  • 1
    Looks like you end up having multiple elements with the same id. If you say `document.getElementById('billAmt')` and I find 10 of them, which one should I return? Maybe only the first one I find? Think about it. – takendarkk Mar 13 '17 at 18:16
  • You can also get it to work on JSFiddle by defining your functions like `window.calc = function(obj) {` – Bryan K Mar 13 '17 at 18:20
  • Thanks guys for your assistance with the jsfiddle. Hey @takendarkk I see what you are referring too and I get that but what would be the best method to be able to calculate each row as its added. When I think about the logic it would be startAmt plus each instance of billAmt is going to equal totalAmt. – Mark White Mar 13 '17 at 18:38
  • jsfiddle working now :) https://jsfiddle.net/zyoy2a6b/19/ – Mark White Mar 13 '17 at 18:50
  • You can use `getElementsByName()` (instead of `getById()`) which will return a collection of all the matching elements. You can then iterate the collection and get each value. – takendarkk Mar 13 '17 at 19:07

1 Answers1

0

Here's a simplified working CodePen of what you want to achieve.

You may want to think about the problem a little differently. It seems you're focused on quick performance (by using calc(this)), but there's a much simpler way of solving your problem that still delivers good performance.

Here's a snippet from my CodePen:

function calc() {
    var money = parseInt(document.querySelector('#money').value) || 0;
    var bills = document.querySelectorAll('table tr input.billAmt') ;
    var billTotal = 0;

    for (i = 0; i < bills.length; i++) {
      billTotal += parseInt(bills[i].value) || 0;
    }

    totalAmt.innerHTML = money - billTotal;
}

var money... and var bills... just grab the inputs from the DOM. billTotal is how much you owe in bills. for... loops through your array (technically node list but whatever) of inputs (in this case all your bills) and adds the input's value to the billTotal. The last line of code just renders the billTotal to the screen.

Edit: I updated the CodePen link above. Now this works with multiple input fields per row. I just changed table tr input to table tr input.billAmt in calc() and bill.classList.add("billAmt"); in addBill(). Also I added || 0 in calc() so you don't get NaN if you leave the input number fields blank. Also I added a description field in addBill() just to prove it works with multiple input fields.

Edit 2 I updated the CodePen. I just added this:

var deleteBtn = document.createElement('button');
deleteBtn.innerHTML = "Delete";
deleteBtn.addEventListener("click", removeRow);
deleteBtn.addEventListener("click", calc);

addEventListener basically does the same thing as onclick except it's a lot more flexible. The order here is important. You want to remove the row, THEN calculate the new cost, otherwise it'll calculate the old cost.

I also added this:

function removeRow(e) {
  e.target.parentNode.remove();
}

Basically when removeRow gets called, it gets called with e (i could've called it event, or anything) as a parameter because it was called from a click event (or any event). e contains useful information, like e.target, which refers to the element that was clicked. .parentNode.remove(); is pretty self-explanatory.

Gabe Rogan
  • 3,343
  • 1
  • 16
  • 22
  • Hey @GabeRogan the code works when you only have 1 cell but it does not seem to like when you have more then one cell. How could I use your calc function to work with mine? Thanks. – Mark White Mar 13 '17 at 21:27
  • Sorry that seems vague but I am referring to adding more than one cell per row. RIght now your code has 1 cell per row and it works but I want a total of 5 cells with the 5th cell actually being a button. – Mark White Mar 13 '17 at 21:37
  • @MarkWhite Got it. I'll update my answer accordingly. – Gabe Rogan Mar 13 '17 at 22:34
  • @MarkWhite I've updated the answer. Basically I just had to give the bill amount inputs a `class` of `billAmt`, but feel free to read through my **Edit** note. – Gabe Rogan Mar 13 '17 at 23:33
  • Hey @GabeRogan, any chance you can help me with auto adjusting the amount if you delete a row? In order for the amount to update you have to delete the number from the cell first than delete it. Otherwise if you just delete the row the ending amount stays the same until you edit another cell number or add a new one. – Mark White Mar 15 '17 at 16:29
  • @MarkWhite sure. I'll update the CodePen and provide an edit note. Basically you should use `addEventListener` instead of `setAttribute` to trigger multiple functions on a click. (like `calc` and `removeRow`) – Gabe Rogan Mar 15 '17 at 19:31
  • @MarkWhite I've updated the answer. See my edit note. – Gabe Rogan Mar 15 '17 at 19:47
  • Took me a minute to figure out how to apply it to my script but I got it! Basically I added the event listener for the removeRow and calc then for the remove function I had to add one more parentNode, Otherwise when I hit the button to delete it would only delete the delete button and leave the rest of the row. function removeRow(e) { e.target.parentNode.parentNode.remove(); } Thanks again man you are awesome! I really appreciate you taking time out of your day to help a total stranger! – Mark White Mar 16 '17 at 20:18