March 21, 201313 yr Hi there, So the project is to create an invoicing tool. I would like a bit of help to see how to go about this, i'm not good with javascript so you may have to bare with me. So i currently have 1 row of 5 text inputs. I can click a button to add another row (Maximum of 10 rows). Each row again has 5 text inputs. So, i need to be able to add a calculation based on input. Structure: Item code > Item Name > Item price > Item quantity > Total Id like Item Quantity to multiply with Item Price and give a total in the Total column and then take the Total columns that have data in them and add it all up for a sub total at the end. Current code i have: $(document).ready(function(){ var counter = 2; $("#addButton").click(function () { if(counter>10){ alert("Only 10 textboxes allowed"); return false; } var newTextBoxDiv = $(document.createElement('tr')) .attr("id", 'TextBoxDiv' + counter); newTextBoxDiv.after().html('<td class="first" id="itmcode"><input placeholder="Insert Item ' + counter + '" class="itmcode" type="text" name="data[' + counter + '][0]" id="invitem' + counter + '" ></td>' + '<td id="itmnme"><input class="itmname" placeholder="Insert Item ' + counter + '" type="text" name="data[' + counter + '][1]" id="invitem' + counter + '" ></td>' + '<td id="itmdesc"><input class="itmdesc" placeholder="Insert Item ' + counter + '" type="text" name="data[' + counter + '][2]" id="invitem' + counter + '" ></td>' + '<td id="itmqty"><input class="itmamnt" id="itm-qty" placeholder="Insert Item ' + counter + '" type="text" name="data[' + counter + '][3]" id="invitem' + counter + '" ></td>' + '<td id="itmamnt"><input class="itmqty" id="itm-amnt" placeholder="Insert Item ' + counter + '" type="text" name="data[' + counter + '][4]" id="invitem' + counter + '" ></td>' + '<td><input type="text" name="total" id="total"/></td>'); newTextBoxDiv.appendTo("#TextBoxesGroup"); counter++; }); $("#removeButton").click(function () { if(counter==1){ alert("No More Rows to Remove"); return false; } counter--; $("#TextBoxDiv" + counter).remove(); }); $("#getButtonValue").click(function () { var msg = ''; for(i=1; i<counter; i++){ msg += "\n Textbox #" + i + " : " + $('#textbox' + i).val(); } alert(msg); }); }); </script> <script type="text/javascript"> $(document).ready(function(){ $('#itm-qty').keyup(calculate); $('#itm-amnt').keyup(calculate); }); function calculate(e) { $('#total').val($('#itm-qty').val() * $('#itm-amnt').val()); } </script> The first row for calculation works spot on. But adding rows there after does not work at all. And i lack an overall totals at the end as well. Any help much appreciated!
March 21, 201313 yr You're creating multiple instances of the same ID (#item-qty and #itm-amnt). You should use the counter increment variable for each new input you're adding, like this: <td><input class="itmqty" id="itm-amn'+ counter + '" placeholder="Insert Item ' + counter + '" type="text" name="data[' + counter + '][4]" id="invitem' + counter + '" ></td> You're also appending every new <td> element with the same ID (id="invitem' + counter + '") which you shouldn't really be doing as ID's should be unique. E.g. <td><input id="inv_itemcode' + counter + '" /></td> <td><input id="inv_itemname' + counter + '" /></td> <td><input id="inv_itemprice' + counter + '" /></td> <td><input id="inv_itemqty' + counter + '" /></td> <td><input id="inv_itemtotal' + counter + '" /></td> To resolve your calculation issue, you could use the class to get the ID of the dynamic row item clicked and then calculate based on that. I'll try and put a fiddle together for you.
March 21, 201313 yr Lyndsey is 100% correct with the above - but the whole problem could be simplified and solved using either class names or, better yet, HTML5 data-* attributes. To take Lyndsey's corrected example above an expand on it: <tr> <td><input data-field="itemcode" /></td> <td><input data-field="itemname" /></td> <td><input data-field="itemprice" /></td> <td><input data-field="itemqty" /></td> <td><input data-field="itemtotal" /></td> </tr>You can then use IDs for each row if you really want to, but I'd suggest using jQuerys .eq(), or just cycling through each row. Quick example or cycling through (untested): var getCalculatedTotal = function(){ var total = 0; $(".product-table tr").each(function(){ var qty = parseInt($(this).find("*[data-field='itemqty']").val()); var price = parseFloat($(this).find("*[data-field='itemprice']").val()); if (!isNaN(qty) && !isNaN(price)){ total += qty * price; } }); return total; }; Edited March 21, 201313 yr by andy9l Wording
March 21, 201313 yr I've put together this for you, it can probably be validated a little bit! But I hope it helps http://jsfiddle.net/LyndseyB/TTjVK/1/ Note: I used the .on() method which is valid for jQuery 1.7 and above. If you're using anything lower, use .live(). Quick explanation: Created each dynamic element with a class along with an ID e.g. <input class= "itmqty" id="#itemqty1" />. Used the class to grab the ID of the input element clicked, and extracted the counter variable e.g. 1 Used the counter variable ($rowNum) to calculate the corresponding Total field e.g. $('#total'+rowNum).val(calc); Update: added running total: http://jsfiddle.net/LyndseyB/TTjVK/2/ Sorry Andy, I didn't realise you'd posted! Hope this helps, Lyndsey.
March 21, 201313 yr An alternative, forked from Lyndsey's approach - just for the benefit of anyone who cares really http://jsfiddle.net/andy9l/re2af/1/ Forgot the 10 limit: http://jsfiddle.net/andy9l/re2af/3/ /Procrastination. Edited March 21, 201313 yr by andy9l
March 21, 201313 yr Nice one Andy ^ First time I've seen the data-field attribute. I've come across something similar (data-role, data-icon etc) using jQuery Mobile but didn't think it existed in HTML. Will have to look that one up Saying that, what am I on about? The data-role attribute was always loaded as part of the HTML. I'm being thick Andy, ignore me! lol Thanks for the link!
March 21, 201313 yr Any element attribute with the data- prefix is "valid". You could have data-andy, data-lyndsey, data-wdf, etc. within most HTML elements (<input>, <a>, etc.). More information is in the latest HTML5 spec draft from W3C: http://goo.gl/kQkxk Edit: For some reason the link was going to some random part of the page - fixed with Goo.gl URL. Edited March 21, 201313 yr by andy9l Fixed link
March 21, 201313 yr Author Awesome! Thanks guys! So how would i go about looping through those results to get a final total of all the Total fields? Iv not tested yet but if the JS fiddle works, im happy. Thanks again!
March 21, 201313 yr Awesome! Thanks guys! So how would i go about looping through those results to get a final total of all the Total fields? Iv not tested yet but if the JS fiddle works, im happy. Thanks again! Updated the fiddle in my previous post to display running total: Update: added running total: http://jsfiddle.net/LyndseyB/TTjVK/2/ You're welcome
March 21, 201313 yr Author Thank you so very much! I am useless at JS but learnt something here i think. Im just looking through to see how its all happening.
March 21, 201313 yr This should work: http://jsfiddle.net/andy9l/re2af/5/ Edited March 21, 201313 yr by andy9l /h2 tag missing :(
March 21, 201313 yr Author i have a header in there for the table as well. <tr><td>Item code</td></tr>... etc. The code in there has find tr:first I changed it to tr:second but it does not work. Any idea how i can get around that?
March 21, 201313 yr i have a header in there for the table as well. <tr><td>Item code</td></tr>... etc. The code in there has find tr:first I changed it to tr:second but it does not work. Any idea how i can get around that? Just add a class to the row e.g. <tr class="cloneRow"> <td><input placeholder="Item Code" name="data[0][0]" class="itmcode" /></td> ..... Then in your jQuery change all references of $('tr:first') to $('tr.cloneRow'). E.g: var row = table.find("tr.cloneRow"); var thisRow = $(this).parents("tr.cloneRow");
March 21, 201313 yr Author That fixed it Lyndsey, i should i have thought about that. One thing i have noticed is (Sorry i should have mentioned this before), the name (data[0][1]), needs to be counting upward as a new row is added. ie: row 1 should be: data[0] row 2: data[1] Also getting £NaN in the totals. I really do appreciate the helping hand as this has been a real headache trying to figure it out.
March 21, 201313 yr You could simply create the input name as an array, for example: <input name="itemcode[]" id="itemcode1" class="itmcode" /> <input name="itemcode[]" id="itemcode2" class="itmcode" /> And then use PHP (or whatever server side language you're using) to loop through the submitted array: foreach( $_POST['itemcode'] as $v ) { print $v; }
March 21, 201313 yr Here's a version with all the problems mentioned above fixed (header row, data[X] increment): http://jsfiddle.net/andy9l/re2af/7/ Edit: Fixed NaN issue. Edited March 21, 201313 yr by andy9l
March 21, 201313 yr Thanks Andy! I'm crap with multi-dimensional arrays! Forking your fiddle for sure
March 21, 201313 yr Important: Problem with NaN final total fixed in this version: http://jsfiddle.net/andy9l/re2af/7/
March 21, 201313 yr Author Here's a version with all the problems mentioned above fixed (header row, data[X] increment): http://jsfiddle.net/andy9l/re2af/7/ Edit: Fixed NaN issue. Sorry to nitpick, but the NaN issue has turned into nothing at all. #total is always £0.00 no matter what. How come it works in jsfiddle yet iv copied it exact and its not working?
March 21, 201313 yr Author <script type="text/javascript"> $(document).ready(function(){ var table = $("#TextBoxesGroup"); var row = table.find("tr").eq(1); var count = 0; $("#addButton").click(function(e){ if (table.find("tr").length >= 10){ alert("Maximum of 10 rows"); return; } var newRow = row.clone(); var regex = new RegExp("data\[[0-9]+\]", "g"); newRow.html(newRow.html().replace(regex, "data[" + (++count) + "]")); table.append(newRow); }); $(document).on('keyup', "*[data-field='quantity'],*[data-field='price']", function(e){ var thisRow = $(this).parents("tr:first"); var rowTotalField = thisRow.find("*[data-field='total']"); var price = parseFloat(thisRow.find("*[data-field='price']").val()); var quantity = parseInt(thisRow.find("*[data-field='quantity']").val()); rowTotalField.val("\u00A3" + (!isNaN(price) && !isNaN(quantity) ? price*quantity : 0).toFixed(2)); var total = 0; table.find("*[data-field='total']").each(function(){ var t = parseFloat($(this).val().replace("£", "")); total += !isNaN(t) ? t : 0; }); $("#total").text(total.toFixed(2)); }); }); $().ready(function() { $(".autof").autocomplete("suggest.php", { width: 260, matchContains: true, selectFirst: false }); }); </script> <table class="intro"id="TextBoxesGroup"> <tr id="tabletop"><td>Item Code</td><td>Item Name</td><td>Item Description</td><td>Price (£)</td><td>Qty</td><td>Total</td></tr> <tr> <td><input placeholder="Item Code" name="data[0][0]" class="autof" /></td> <td><input name="data[0][1]" class="auto" placeholder="Item Name" /></td> <td><input name="data[0][2]" class="auto" placeholder="Item Description" /></td> <td><input data-field="price" name="data[0][3]" placeholder="Item Amount" class="auto" /></td> <td><input data-field="quantity" name="data[0][4]" placeholder="Item Quantity" class="itmqty" /></td> <td><input data-field="total" name="data[0][5]" placeholder="Item Total" readonly="readonly" /></td> </tr> </table> <h2>Total Cost: £<span id="total">0.00</span></h2> <input type='button' value='Add Item' id='addButton'> <input type='button' value='Remove Item' id='removeButton'><br/> Send to Client: <input <?php echo $emailit; ?> type="checkbox"/><br/><br/> <input type="submit" value="Send" />
March 21, 201313 yr If I copy and paste that code, it works. That would suggest your autocomplete plugin is causing troubles. Check console errors, post them here.
March 21, 201313 yr Author No worries, i fixed it. It was another plugin i was using conflicting. Thanks again guys!
May 28, 20206 yr Hi, Found this code still relevant today. I need help with delete button on each row which i added, but it doesn't reset total if any rows are deleted after updating fields. Any help on same largely appreciated. Edited May 28, 20206 yr by pravinc
August 31, 20205 yr Hello, I've been looking at this and was hoping someone can help. I want to add in the JS code VAT rate at a fixed rate and than have some fields at the bottom do sub total and Vat and Total. Can you please show the how the code can be updated to include these fields also. Thank You
Create an account or sign in to comment