JS Function only appends one row - javascript

I would like my js to add a row every time the user adds their initials. My code works the first time the user enters their initials, a new row is created under the first row, but then does not add anymore. I am not sure what i need to do.
My JS code:
(function(){
var counter = 1;
$("#preformedBy").change(function(){
$('#timeStamp').html(new Date().toLocaleString());
$('#harvestedCannabis > tbody:last-child').append('<tr><td><input type="number" id="toteNum" readonly></td><td><input type="number" step=".1">' +
'</td><td><input type="number" step=".1"></td><td><input type="number" step=".1"></td><td><input type="number" step=".1"></td><td>' +
'<input type="text"></td><td><input type="text" id="preformedBy"></td><td id="timeStamp"><input type="text" readonly></td></tr>');
counter = counter + 1;
$('#toteNum').html(counter)
})
});
My HTML:
<table id="harvestedCannabis">
<tr>
<th>Tote #</th>
<th>Flowers</th>
<th>Trim A</th>
<th>Trim B</th>
<th>Waste</th>
<th>Originating Line(A,B,C)</th>
<th>Preformed By</th>
<th>Time Stamp</th>
</tr>
<tr>
<td id="toteNum"><input type="number" value="1" readonly></td>
<td><input type="number" step=".1"></td>
<td><input type="number" step=".1"></td>
<td><input type="number" step=".1"></td>
<td><input type="number" step=".1"></td>
<td><input type="text"></td>
<td><input type="text" id="preformedBy"></td>
<td id="timeStamp"><input type="text" readonly></td>
</tr>
</table>
I would like the table to keep appending new row as many times as the user requires, automatically after they insert their initials

You have two main issues with your code. I'll explain them, but first take a look at this fiddle I made with your code, this version actually works:
https://jsfiddle.net/49Ln0qcf/#&togetherjs=2hdIlfBaC0
So now lets explore where you went wrong:
1.
You're listening to an ID attribute for your onChange event. It works the first time around because at that point in time you only have one element with id="preformedBy", but after you add your second row you then have TWO elements with the same ID. jQuery is listening to the first instance of the ID in the dom, thus your additional input fields are not being listened to. So, replace your id attribute with a class, then your .preformedBy selector will work past your second iteration.
2.
Taking the step above alone will not fix your code. To get more than the first iteration to work, you'll need to listen to the parent element of your .preformedBy class. In your code, you're listening to preformedBy directly like so:
$("#preformedBy").change(function(){
The problem here is that on page load, jquery is only aware of the dom elements that exist on page load, thus the first and only instance of preformedBy. So jQuery will continue to listen to that element and ONLY that element because it knows nothing of the elements you've added to the dom AFTER page load. In order to get jQuery to listen to ALL instances of preformedBy, you need to listen to a parent selector. Notice in my code, I'm listening to preformedBy like so:
$("#harvestedCannabis").on('change', '.preformedBy', function(){
The key difference is that my code listens to change events on the parent element, but specifically listens to any and all occurrences of '.preformedBy' within that parent element. As where your code is listening to a unique element with the preformedBy selector.
I hope this helps.
Finalized code that works:
html:
<table id="harvestedCannabis">
<tr>
<th>Tote #</th>
<th>Flowers</th>
<th>Trim A</th>
<th>Trim B</th>
<th>Waste</th>
<th>Originating Line(A,B,C)</th>
<th>Preformed By</th>
<th>Time Stamp</th>
</tr>
<tr>
<td id="toteNum1"><input type="number" value="1" readonly></td>
<td><input type="number" step=".1"></td>
<td><input type="number" step=".1"></td>
<td><input type="number" step=".1"></td>
<td><input type="number" step=".1"></td>
<td><input type="text"></td>
<td><input type="text" class="preformedBy"></td>
<td id="timeStamp1"><input type="text" readonly></td>
</tr>
</table>
jQuery:
var counter = 1;
$("#harvestedCannabis").on('change', '.preformedBy', function(){
$('#timeStamp'+counter).html(new Date().toLocaleString());
$('#harvestedCannabis > tbody:last-child').append('<tr><td><input type="number" id="toteNum'+(counter + 1)+'" readonly></td><td><input type="number" step=".1">' +
'</td><td><input type="number" step=".1"></td><td><input type="number" step=".1"></td><td><input type="number" step=".1"></td><td>' +
'<input type="text"></td><td><input type="text" class="preformedBy"></td><td id="timeStamp'+(counter + 1)+'"><input type="text" readonly></td></tr>');
$('#toteNum'+(counter + 1)).val(counter + 1);
counter = counter + 1;
});

Your are changing all this element #toteNum with counter value
<td id="toteNum"><input type="number" value="1" readonly></td>
When you create a new element you need to rebind the listeners.
When your code works, your HTML will have 2 #preformedBy elements, you may have unique ids on the same page.

Try the insertRow() and insertCell() methods.
According to W3Schools example:
https://www.w3schools.com/jsref/met_table_insertrow.asp

Remove the id from your html and js code and use a class selector to trigger the change function, then it should work.

Related

jQuery set dynamically created input fields based on difference between 2 fields

Is there a way I can set input values of each dynamically created input fields based on a difference between 2 dynamically created fields. Below is my code and what am trying to achieve.
HTML
<table id="requested-wh-stock-table" class="table table-bordered table-hover dataTable" width="100%">
<thead>
<tr>
<th>Luminaire</th>
<th>Order Quantity</th>
<th>Delivered Qty</th>
<th>Back Order Qty</th>
</tr>
</thead>
<tbody>
#foreach ($salesorder as $request)
<tr>
<td><input type="text" class="form-control" name="luminaire" value="{{$request->luminaire}}" readonly></td>
<td><input type="number" class="form-control" name="order_quantity" id="order_quantity"
value="{{$request->quantity}}" readonly /></td>
<td><input type="number" class="form-control" name="delivered_quantity" id="delivered_quantity" value="" />
</td>
<td><input type="number" class="form-control" name="backorder_quantity" id="backorder_quantity" value=""
readonly /></td>
</tr>
#endforeach
</tbody>
</table>
jQuery
//Update Backorder qty based on Delivered Quantity amount
$(document).ready(function () {
$("#delivered_quantity").change(function () {
var backorder_quantity = $("#order_quantity").val() - $("#delivered_quantity").val();
$("#backorder_quantity").val(backorder_quantity);
});
});
Currently, it only updates the first field, it does not update other fields, is there a way I can loop through all fields and update Backorder field as I change the Delivered Quantity field?
If you are dynamically updated these fields within a loop, you will run into naming conflicts with the IDs. Instead, I would set the class name of each of the fields, and not the IDs:
<tr>
<td><input type="text" class="form-control" name="luminaire" value="10" readonly></td>
<td><input type="number" class="form-control order_quantity" name="order_quantity" id="order_quantity" value="20" readonly/></td>
<td><input type="number" class="form-control delivered_quantity" name="delivered_quantity" id="delivered_quantity" value=""/></td>
<td><input type="number" class="form-control backorder_quantity" name="backorder_quantity" id="backorder_quantity" value="" readonly/></td>
</tr>
Now you can attach an event listener in jQuery to every element with that class name, and you can reference it using the "this" keyword. Then sense they all have a parent that is a table row, we can use that to refer to the other input elements that are children of that parent element. Once we find the children in the row of the elements that you're targeting, we can update the values:
$(document).ready(function() {
$('.delivered_quantity').on('change', function() {
let parent = $(this).parents('tr');
let order_quantity = parent.find('.order_quantity');
let backQuant = parent.find('.backorder_quantity');
let backorder_quantity = order_quantity.val() - $(this).val();
backQuant.val(backorder_quantity);
});
});
We attach the change event to EACH of the delivered_quantity elements that you could potentially be targeting in your table. When any of them experience this event, the callback function above will occur. Next we get the parent element that is a table row. We then get the order_quantity and backorder_quantity elements that are children within the table row. We then calculate the backorder_quantity variable and then update the backorder_quantity element to match that value. Note that I had to put in some random values for your luminair and order_quantity sense we don't know what those values will be sense you're using them in a loop.
You should not use id on elements which are not unique. Since the inputs will be rendered multiple times (in rows), then I suggest to not use the id attribute. So let's try to use the name attribute instead for finding the DOM.
On the change event listener of delivered_quantity input, get the other fields in the same row. To do that, from this object inside the event handler, do seek the closest tr and then find the particular field from it's child.
$(document).ready(function() {
$("[name=delivered_quantity]").change(function() {
var order_quantity = $(this).closest('tr').find("[name=order_quantity]").val()
var delivered_quantity = $(this).closest('tr').find("[name=delivered_quantity]").val();
var backorder_quantity = order_quantity - delivered_quantity
$(this).closest('tr').find("[name=backorder_quantity]").val(backorder_quantity);
});
});

How can I add a time stamp to a table row automatically and increment the tote# on change

I need to add the timestamp automatically after the user enters their initials, then the new line will be created and and I will need to increase the tote# of that next line to the next number eg.(on line 2 tote# will be 2)
This is my table structure
<table id="harvestedCannabis">
<tr>
<th>Tote #</th>
<th>Flowers</th>
<th>Trim A</th>
<th>Trim B</th>
<th>Waste</th>
<th>Originating Line(A,B,C)</th>
<th>Preformed By</th>
<th>Time Stamp</th>
</tr>
<tr>
<td>1</td>
<td><input type="number" step=".1"></td>
<td><input type="number" step=".1"></td>
<td><input type="number" step=".1"></td>
<td><input type="number" step=".1"></td>
<td><input type="text"></td>
<td><input type="text" id="preformedBy"></td>
<td id="timeStamp"></td>
</tr>
</table>
and here is my JS
$(document).ready(function(){
$("#preformedBy").change(function(){
$('#harvestedCannabis > tbody:last-child').append('<tr><td>1</td><td><input type="number" step=".1"></td><td><input type="number" step=".1"></td><td><input type="number" step=".1"></td><td><input type="number" step=".1"></td><td><input type="text"></td><td><input type="text" id="preformedBy"></td><td id="timeStamp"></td></tr>');
});
});
I would like the timestamp to be enter automatically and tote# to increase when the user enters their initails
you can use underscorejs debounce
$('#preformedBy').keyup(_.debounce($('#timeStamp').html(new Date()), 500));
for more on debouce with lodash you can read
https://lodash.com/docs/#debounce
if you dont want to use underscore you can use debounce with jquery read this https://code.google.com/archive/p/jquery-debounce/
and if you just want key up simply you can use
$('#preformedBy').keyup(()=>{
$('#timeStamp').html(new Date())
});
further you can also spice it up with setTimeout to wait for some user delay to confirm has he finished or not
increment follows same logic just change the targeting html #id
personally i am big fan of lodash one liners are always better to read and debug

Automatic multiplication for several row

I'm new on coding, then any help will greatly appreciated.
I'm trying to make automatic multiplication from 2 value. Basically my table looks like this. Multiplication works perfectly on the first row. If I make another row by simply copying this code:
<tr>
<td><input id="box1" type="text" oninput="calculate()" /></td>
<td><input id="box2" type="text" oninput="calculate()" /></td>
<td><input id="result" /></td>
</tr>
then the second row won't work. This may happen because the id on second row exactly same with the first row. But if I change the id, the script won't work either. Would you please show me how to fix it?
EDIT: I use this scrip for multiplication purpose:
function calculate() {
var myBox1 = document.getElementById('box1').value;
var myBox2 = document.getElementById('box2').value;
var result = document.getElementById('result');
var myResult = myBox1 * myBox2;
result.value = myResult;
}
I would bind a single input event handler to the table, which will catch any input events on any of the table cells' input elements. Within the handler event.target will refer to the input element where the event originated, and you can use DOM navigation properties/methods to find the associated table cells in the same row.
Maybe a little something like this, using class instead of id:
document.getElementById("multiplier").addEventListener("input", function(e) {
var row = e.target.parentNode.parentNode
var val1 = row.querySelector(".valOne").value
var val2 = row.querySelector(".valTwo").value
row.querySelector(".result").value = val1 * val2
})
<table id="multiplier">
<tr>
<td><input class="valOne" type="text" /></td>
<td><input class="valTwo" type="text" /></td>
<td><input class="result" /></td>
</tr>
<tr>
<td><input class="valOne" type="text" /></td>
<td><input class="valTwo" type="text" /></td>
<td><input class="result" /></td>
</tr>
<tr>
<td><input class="valOne" type="text" /></td>
<td><input class="valTwo" type="text" /></td>
<td><input class="result" /></td>
</tr>
</table>
Further reading:
.addEventListener() method
.parentNode property
.querySelector() method
It would be better to use e.target.closest("tr") instead of e.target.parentNode.parentNode, but note that the .closest() method isn't supported in IE so you would need to use a polyfill.
Note that the JS that I've shown would need to be in a script element that is after the table (e.g., at the end of the body right before the closing </body> tag), or you'd need to wrap it in a document load or DOMContentLoaded handler.

jquery / javascript to sum field only if checkbox on same row is checked

I have a jquery / javascript function that totals the number of cubes in my order. this works 100% and is below.
function calculateTotalVolume() {
var grandTotalCubes = 0;
$("table.authors-list").find('input[name^="cubicvolume"]').each(function () {
grandTotalCubes += +$(this).val();
});
$("#grandtotalcubes").text(grandTotalCubes.toFixed(2));
}
as mentioned the above works great. I need a second function to total the same field but only if an checkbox named treated is checked. each row has the checkbox named treated but as the table is dynamically generated, a counter is appended to the name each time hence my use of name^="treated"
I am after something like below but this doesn't work:
function calculateTotalTreatedVolume() {
var grandTotaltreatedCubes = 0;
$("table.authors-list").find('input[name^="cubicvolume"]').each(function () {
if($("table.authors-list").find('checkbox[name^="treated"]').checked){
alert('10');
grandTotaltreatedCubes += +$(this).val();
}
});
$("#grandtotaltreatedcubes").text(grandTotaltreatedCubes.toFixed(2));
}
help appreciated as always.
UPDATE
Rendered HTML output [1 dynamic row added]: (Still in development so very rough, please excuse it)
<table class="authors-list" border=1>
<thead>
<tr>
<td></td><td>Product</td><td>Price/Cube</td><td>Qty</td><td>line total cost</td><td>Discount</td><td>Cubes per bundle</td><td>pcs per bundle</td><td>cubic vol</td><td>Bundles</td><td><input type="checkbox" class="checkall"> Treated</td>
</tr>
</thead>
<tbody>
<tr>
<td><a class="deleteRow"> <img src="http://devryan.tekwani.co.za/application/assets/images/delete2.png" /></a></td>
<td><input type="text" id="product" name="product" />
<input type="hidden" id="price" name="price" readonly="readonly"/></td>
<td><input type="text" id="adjustedprice" name="adjustedprice" /></td>
<td><input type="text" id="qty" name="qty" /></td>
<td><input type="text" id="linetotal" name="linetotal" readonly="readonly"/></td>
<td><input type="text" id="discount" name="discount" /></td>
<td>
<input type="text" id="cubesperbundle" name="cubesperbundle" >
</td>
<td>
<input type="text" id="pcsperbundle" name="pcsperbundle" >
</td>
<td>
<input type="text" id="cubicvolume" name="cubicvolume" size='5' disabled>
</td>
<td><input type="text" id="totalbundles" name="totalbundles" size='5' disabled ></td>
<td valign="top" ><input type="checkbox" id="treated" name="treated" ></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="15"><input type="button" id="addrow" value="Add Product" /></td>
</tr>
<tr>
<td colspan="3">Grand Total: R<span id="grandtotal"></span></td>
<td colspan="2">Ave Discount: <span id="avediscount"></span>%</td>
<td colspan="1">Total Cubes: <span id="grandtotalcubes"></span></td>
<td colspan="15">Treated Cubes: <span id="grandtotaltreatedcubes"></span></td>
</tr>
<tr>
<td colspan="15"><textarea rows="1" cols="50" placeholder="Specific Comments"></textarea><textarea rows="1" cols="20" placeholder="Customer Reference"></textarea>
</td>
</tr>
</tfoot>
</table>
First go the parent tr and then using find to find the checkbox in current row and also use checked with DOM object not jQuery object, you can use indexer to convert jQuery object to DOM object.
Change
if($("table.authors-list").find('checkbox[name^="treated"]').checked){
To
if($(this).closest('tr').find('checkbox[name^="treated"]')[0].checked){
checked is a property of the actual DOM element, and what you have is a jQuery element. You need to change this:
$("table.authors-list").find('checkbox[name^="treated"]').checked
To this:
$("table.authors-list").find('checkbox[name^="treated"]')[0].checked
-^- // get DOM element
Or more jQuery-ish:
$("table.authors-list").find('checkbox[name^="treated"]').is(':checked')
You can iterate through the "checked" checkboxes using $("table.authors-list").find('checkbox[name^="treated"]:checked') and use the value of the input nearest to it (assumed to be in the same row).
Assuming your table has many rows each having a checkbox and an input, you can use:
function calculateTotalTreatedVolume() {
var grandTotaltreatedCubes = 0;
// iterate through the "checked" checkboxes
$("table.authors-list").find('input[type="checkbox"][name^="treated"]:checked').each(function () {
// use the value of the input in the same row
grandTotaltreatedCubes += +$(this).closest('tr').find('input[name^="cubicvolume"]').val();
});
$("#grandtotaltreatedcubes").text(grandTotaltreatedCubes.toFixed(2));
}
Try this:
var grandTotaltreatedCubes = 0;
// Cache the table object here for faster processing of your code..
var $table = $("table.authors-list");
$table.find('input[name^="cubicvolume"]').each(function () {
// Check if checkbox is checked or not here using is(':checked')
if ($table.find('checkbox[name^="treated"]').is(':checked')) {
grandTotaltreatedCubes += $(this).val();
}
});
$("#grandtotaltreatedcubes").text(grandTotaltreatedCubes.toFixed(2));
Change the following line
if($("table.authors-list").find('input[name^="treated"]').checked){
To this
if($("table.authors-list").find('input[name^="treated"]').is(':checked')){

How to add row in html table on top of specific row?

I have a table, and each row has a button to add a new row on top of it. Each row has new inputs.
I know how to add a row on top of the table, but not on top of each row that I'm clicking on the button. Would anyone have a tip on how to solve it? I might be able to do it, but the solution I see is very complicated, and I'm sure there must be a smarter solution.
Oh, also I don't know how to update the parameter sent in the insertNewRow(id) function.
So far this is what I have:
<script type="text/javascript">
function insertNewRow(id){
var row = document.getElementById("bottomRow");
var newrow = row.cloneNode(true);
console.log(newrow);
var newInputs = newrow.getElementsByTagName('input');
var allRows = row.parentNode.getElementsByTagName('tr');
row.parentNode.insertBefore(newrow, row);
var i=row.rowIndex;
console.log(i);
}
</script>
<table id="myTable">
<tr>
<td>Title1:</td>
<td></td>
<td>Title2:</td>
<td></td>
<td>Title3:</td>
<td></td>
<td></td>
</tr>
<tr>
<td><input class="c1" readonly maxlength="9" size="7" id="gTop" type="text" value ="11"></td>
<td> <-></td>
<td id="l1"><input class="c2" style="width:35px;" maxlength="9" size="7" type="text" id="lTop" value="33"></td>
<td>=</td>
<td id="rv1"><input id="rvTop" input class="c2" style="width:105px;" maxlength="100" size="37" type="text" value="blahblahblah"></td>
<td></td>
<td>x</td>
</tr>
<tr id="bottomRow">
<td><input class="c1" readonly maxlength="9" size="7" id="gBottom" type="text" value =""></td>
<td> </td>
<td id="l1"><input class="c2" style="width:35px;" maxlength="9" size="7" type="text" id="lBottom" value="11"></td>
<td>=</td>
<td id="rv1"><input id="rvBottom" input class="c2" style="width:105px;" maxlength="100" size="37" type="text" value="blahblahblah"></td>
<td><button type="button" onclick="insertNewRow(1)">+</button></td>
<td>x</td>
</tr>
</table>
In the onclick attribute, instead of just calling insertNewRow(), do something like
insertNewRow.apply(this);
The this keyword inside the onclick attribute is a reference of the clicked element. With insertNewRow.apply(this), we'll be calling insertNewRow() and at the same time, assign the this keyword inside that function call to the clicked element or in this case, the button (if we don't do that, this inside insertNewRow() will be a reference to the Window object instead). Then in, your insertNewRow() function, check if the current element being clicked on is a tr element. If not, go up by one level and see if that element is a tr element. Keep doing that until you get to the first tr element. So, basically you'll be searching for the closest tr element.
<button type="button" onclick="insertNewRow.apply(this);">+</button>
function insertNewRow(){
var row = null,
el = this;
// Get the closest tr element
while (row === null)
{
if (el.tagName.toLowerCase() === 'tr')
{
row = el; // row is now the closest tr element
break;
}
el = el.parentNode;
}
// Rest of the code here
}​
JsFiddle
If you're still not sure what Function.apply() is, take a look at the documentation here.

Categories