Javascript: calculate appended value - javascript

I have this table which adds service. Im trying to input evry quantity and display total on keyup on the right.
I can only get the first row working not the next rows.
Here are my codes
$(document).on('keyup',function(){
var pri = $('#price_display').val();
var q = $('#qty_display').val();
var tots = pri*q;
$('#sub_display').val(tots);
});
every row is added through append
price_display holds the price value
qty_display holds the inputted value
sub_dispplay holds the output value

I'm willing to bet each row has the same ID for each column (e.g. id=price_display). IDs by definition need to be unique on each page, so jQuery always finds the first instance of each (i.e. the first row).
Probably the easiest way around this is to append a row number to each field, e.g. price_display_1, qty_display_1, sub_quantity_1 etc. - that way jQuery can look for the right instance of each field.
See below for an example - although having said all this, it looks as though you're using something like Angular Material to style your page, so it might be worth your time investigating databinding in AngularJS to save you the hassle.
// source data for table (array holds a JavaScript object per row)
var tableData = [{
serviceCode: 'xbed',
serviceName: 'xbed',
price: 500,
quantity: 1
},
{
serviceCode: 'xpil',
serviceName: 'xpil',
price: 200,
quantity: 2
}
];
// find the table body
var $tableBody = $("#myTable").children("tbody");
// for each row of data, generate a table row
tableData.forEach(function(row, index) {
var rowIndex = index + 1;
$tableBody.append("<tr>" +
"<td><input type='text' id='service_code_" + rowIndex + "' value='" + row.serviceCode + "' /></td>" +
"<td><input type='text' id='service_name_" + rowIndex + "' value='" + row.serviceName + "' /></td>" +
"<td><input type='text' class='price' id='price_display_" + rowIndex + "' value='" + row.price + "' /></td>" +
"<td><input type='text' class='qty' id='qty_display_" + rowIndex + "' value='" + row.quantity + "' /></td>" +
"<td><input type='text' class='total' id='sub_display_" + rowIndex + "' value='" + row.price * row.quantity + "' /></td>" +
"</tr>");
});
// wire up keyboard handlers
$(".price, .qty").on("keyup", function(evt) {
var $row = $(evt.target).closest("tr"),
price = $row.find(".price").first().val(),
qty = $row.find(".qty").first().val(),
$total = $row.find(".total").first();
$total.val(price * qty);
});
table {
font-family: 'Arial', sans-serif;
}
input {
width: 8em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="myTable">
<thead>
<tr>
<th>Service code</th>
<th>Service name</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
</tr>
</thead>
<tbody>
</tbody>
</table>

Change ids to classes and try that code:
$(document).on('keyup', '.qty_display, .price_display', function(){
var row = $(this).parent('tr');
var pri = row.find('.qty_display').val();
var q = row.find('.price_display').val();
var tots = pri*q;
row.find('.sub_display').val(tots);
});
In that way you will change only need rows with correct values.

try to log "tots" variable value, is it coming?
if YES
**THEN**
make sure must have unique "#sub_display" id to display the value else it only appears on the first raw
if NO
**THEN**
try to log the second raw object eg:-console.log(this)

Related

How to increment an array with each dynamically created table row in JavaScript

I'm dynamically creating rows in a table with 3 columns: Item, Description & QTY. How do I increment an array with each dynamically created table row?
Here's the results I'm getting when submitting the form below containing two rows with the following values.
The first row values are: Item1, Description1, Qty1
and the second row values are: Item2, Description2, Qty2
1=Item1&1=Description1&1=QTY1&1=Item2&1=Description2&1=QTY2
I would like to return the following instead:
1=Item1&1=Description1&1=QTY1&2=Item2&2=Description2&2=QTY2
https://jsfiddle.net/rimshot609/zgdvxo83/4/
<form name="SiteForm" id="SiteForm" method="post" action="mailto:test#test.com">
<div class="line-item">
<fieldset>
<table id="textbox" border="0">
<tbody>
<tr>
<td>
</td>
</tr>
</tbody>
</table>
<input type="itembutton" onclick="createTableRows()" value="Add Item" />
</fieldset>
</div>
<input type="submit" name="subform" value="Submit Form" />
<script>
function createTableRows() {
var someText = 'Item, Name, Qty,'
var table = document.getElementById("textbox");
var rowlen = table.rows.length;
var row = table.insertRow(rowlen);
row.id = rowlen;
for (i = 0; i < 2; i++) {
arr = ['1']; //I need this number to increase by 1 with each table row created.
var x = row.insertCell(i)
if (i == 1) {
x.innerHTML = "<input type='button' onclick='removeCell(" + row.id + ")' value=Delete>"
} else {
x.innerHTML = "<input type='textbox' placeholder='Item Number' name='" + arr[i] + "' required='required'><input type='textbox' placeholder='Description' name='" + arr[i] + "' required='required'><input type='textbox' placeholder='QTY' name='" + arr[i] + "' required='required'>"
}
}
}
function removeCell(rowid) {
var table = document.getElementById(rowid).remove();
}
</script>
</form>
Without deletion, it's very simple. Just replace this line:
arr = ['1']; //I need this number to increase by 1 with each table row created.
With this:
arr = [row.id]; //I need this number to increase by 1 with each table row created.
row.id is always set to table.rows.length.
But when you introduce Deletion into the equation things get more complicated. Each time you delete a row you'll want to either change the value for the existing rows, or use another value that you increment differently.
The first solution feels quite elegant, but with the way this has been set up would be a little clunky to implement. The other would require something like:
let highestValue = 0;
function createTableRows() {
highestValue++;
var someText = 'Item, Name, Qty,'
var table = document.getElementById("textbox");
var rowlen = table.rows.length;
var row = table.insertRow(rowlen);
row.id = highestValue;
The problem is that you'll have gaps. If you have rows 1, 2 and 3 then delete 2, the results will jump from 1 to 3.

Remove items from array when removing from table

I have a function that builds up a table of dates as the user clicks on different dates in a date picker.
An array is also built up of the dates as they are added to the table
I also have a function to remove the dates from the table and array as they are clicked in the table
This all works except the last part. The dates are removed from the table but not the array and this is what I need help with.
I don't think it is appending
//function to build up custom dates list
var customStartDates = [];
var customEndDates = [];
$("#btnStartDate").on('click', function () {
//I think this.value below is undefined..
$('#customDatesTable').append("<tr id='" + this.value + "'><td>" +
$("#StartDateCustom").val() + "<input type='hidden'
name='CustomStartDates[]' value='"
+ $("#StartDateCustom").val() + "'>" + "<input type='hidden'
name='CustomEndDates[]' value='"
+ $("#EndDateCustom").val() + "'>" + "</td>" + "<td>"
+ $("#EndDateCustom").val() + "</td>" + "<td
width='10%'>X</td> </tr>");
customStartDates.push( $("#StartDateCustom").val());
customEndDates.push($("#EndDateCustom").val());
});
//function to remove custom dates from table
$("#customDatesTable").on('click', 'td', function () {
var item = $(this).parent().attr('value');
$(this).parent().remove();
customStartDates = $.grep(customStartDates, function (value) {
return value != item;
});
customEndDates = $.grep(customEndDates, function (value) {
return value != item;
});
});
You should read id attribute of <TR>, as <TD> parent's can only be <TR> which doesn't have value attribute.
var item = $(this).parent().attr('id');
Send Start Date or End Date as Input parameter For a Function and Splice that Selected Date Index From the array.
Try this for both Start and End Dates
function DeleteStartDate(StartDate) {
for (var i = 0; i < customStartDates.length; i++) {
if (StartDate == customStartDates[i]) {
customStartDates.splice(i, 1);
$("#btnStartDate").click();
}
}
}

Node js not recognizing input fields that were added dynamically to the form

I saw a similar question but did not know how to apply it to node js. I created a cart, a user clicks to add an item and that item is added to the form using jquery. On submit however, the only form field acknowledge is the totalCost which was not dynamically added.
My pug file (the relevant portion):
h1#spendable 50000
table
form#cartitems(method='POST' action='/store')
tbody
tr#buyit(style='border-bottom:0px solid white; height:50px; display:none;')
td#purchCost <b> Total: </b>
input#costcost.minIn.nolineinput(name='totalCost' type='text' readonly)
tr
td(colspan=2)
button(type='submit' style='width:100%; padding:5px 0px;') Buy
My javascript file looks like this:
function addToCart(butt){
var $spendable = $('#spendable');
var balance = Number( $spendable.text() );
var itmCost = Number($(butt).prev().prev().text());
var boughtItems = $('.firstline').length;
if(balance > itmCost && boughtItems < 4){
var imgSrc = $(butt).parent().prev().attr('src');
var specific = $(butt).prev().val();
var specificL= $(butt).prev().text();
var itmName = $(butt).prev().prev().prev().text();
var itmType = $(butt).parent().parent().parent().prop('id');
var purchCost= Number( $('#purchCost .minIn').val() );
//create a demo
if (itmType == 'Treat' || itmType == 'Prize'){
alert(itmType);
$('#cartcontainer table tbody').prepend("<tr class='firstline'><td class='smtd' rowspan=2 ><img class='demo' src='" + imgSrc + "'></img></td><td><input type='text' class='nolineinput' name='itmName' value='" + specific +"|" + itmName + "' readonly></input></td><tr><td><div class='minidiv'><p>" + Number(itmCost) + "</p></div><div class='microdiv'><input id='chck' type = 'checkbox' name = 'itmKind' onclick='remove(this)' checked></input></div></td></tr>")
if(boughtItems ==0){
$('#buyit').show()
}
}else{
$('#cartcontainer table tbody').prepend("<tr class='firstline'><td class='smtd' rowspan=2 ><img class='demo' src='" + imgSrc + "'></img></td><td><input type='text' class='nolineinput' name='itmName' value='" + itmName +"| " + specificL + "' readonly></input></td><tr><td><div class='minidiv'><p>" + Number(itmCost) + "</p></div><div class='microdiv'><input class='chck' type = 'checkbox' name = 'itmKind' value='" + itmCost + "' onclick='remove(this)' checked></input></div></td></tr>");
if(boughtItems ==0){
$('#buyit').show()
}
}
var total= purchCost + itmCost;
$('#purchCost .minIn').val(total);
$spendable.text( balance-itmCost);
}else if (boughtItems >= 4){
alert("You've reached the max amount of item\n you can purchase. Buy more next week!")
}else {
alert('Sorry you do not have enough\n points to purchase this item.')
}
}
Also I should mention I'm using body-parser and when i console.log( req.body) the only thing i get back is {totalCost:'1500'} but what I wish to see is: {itmName:'Caprisun', itmKind:'Cherry', totalCost:'1500'}
Nevermind solved it on my own. The table should have been created inside the form. Table, tbody , tr, td should've all been together like so:
form
table
tbody
tr
td Total
td
input(type='text' name='itemName')

How to get the value of dynamically created tablerow?

I tried to get the value of the input-field that was dynamically created in function rijToevoegen().
Somehow I keep getting undefined, what am I doing wrong?
These are my functions I use:
//adding the tablerow
function rijToevoegen(columnarray, fieldarray, tabelnaam){
var columns = columnarray;
var fields = fieldarray;
var row = '<tr>';
for(i=0;i<columns.length;i++){
row += "<td class=columns[i]><input type='text' id=fields[i]></td>";
console.log(fields[i]);
}
row += '</tr>';
$(tabelnaam).append(row);
}
//getting the value
$('#vs_opslaan').click(function() {
var columns = ['naamkolom','locatiekolom','hostkolom','cpukolom','memorykolom','oskolom','hddkolom','spkolom','usernamekolom','passwordkolom','ipkolom','domeinkolom','opmerkingenkolom'];
var velden = ['naamveld','locatieveld','hostveld','cpuveld','memoryveld','osveld','hddveld','spveld','usernameveld','passwordveld','ipveld','domeinveld','opmerkingenveld'];
var response_array = [];
for(i=0;i<velden.length;i++){
var rij = $('#velden[i]').val();
console.log(rij);
//response_array += $().value;
}
//console.log(response_array);
});
Help is always appreciated!
Ramon
When you are referencing the arrays they cannot be inside the string or they will be set as literal strings.
When you put "<td class=columns[i]><input type='text' id=fields[i]></td>"the class and id contain literal strings columns[i] and fields[i]
What you want is to concatenate your array values with the markup.
"<td class=" + columns[i] + "><input type='text' id=" + fields[i] + "></td>"
This also applies to when you are doing the jquery selector.
So instead of $('#velden[i]').val(); you want $('#' + velden[i]).val();

Dynamic creation of large html table in javascript performance

I have an application which is used for data analysis and I'm having a few performance issues with the creation of the table. The data is extracted from documents and it is important that all data is presented on one page (pagination is not an option unfortunately).
Using jQuery, I make an ajax request to the server to retrieve the data. On completion of the request, I pass the data to an output function. The output function loops through the data array using a for loop and concatenating the rows to a variable. Once the looping is complete, the variable containing the table is then appended to an existing div on the page and then I go on to bind events to the table for working with the data.
With a small set of data (~1000-2000 rows) it works relatively good but some of the data sets contain upwards of 10,000 rows which causes Firefox to either crash and close or become unresponsive.
My question is, is there a better way to accomplish what I am doing?
Here's some code:
//This function gets called by the interface with an id to retrieve a document
function loadDocument(id){
$.ajax({
method: "get",
url: "ajax.php",
data: {action:'loadDocument',id: id},
dataType: 'json',
cache: true,
beforeSend: function(){
if($("#loading").dialog('isOpen') != true){
//Display the loading dialog
$("#loading").dialog({
modal: true
});
}//end if
},//end beforesend
success: function(result){
if(result.Error == undefined){
outputDocument(result, id);
}else{
<handle error code>
}//end if
if($('#loading').dialog('isOpen') == true){
//Close the loading dialog
$("#loading").dialog('close');
}//end if
}//end success
});//end ajax
};//end loadDocument();
//Output document to screen
function outputDocument(data, doc_id){
//Begin document output
var rows = '<table>';
rows += '<thead>';
rows += '<tr>';
rows += '<th>ID</th>';
rows += '<th>Status</th>';
rows += '<th>Name</th>';
rows += '<th>Actions</th>';
rows += '<th>Origin</th>';
rows += '</tr>';
rows += '</thead>';
rows += '<tbody>';
for(var i in data){
var recordId = data[i].id;
rows += '<tr id="' + recordId + '" class="' + data[i].status + '">';
rows += '<td width="1%" align="center">' + recordId + '</td>';
rows += '<td width="1%" align="center"><span class="status" rel="' + recordId + '"><strong>' + data[i].status + '</strong></span></td>';
rows += '<td width="70%"><span class="name">' + data[i].name + '</span></td>';
rows += '<td width="2%">';
rows += '<input type="button" class="failOne" rev="' + recordId + '" value="F">';
rows += '<input type="button" class="promoteOne" rev="' + recordId + '" value="P">';
rows += '</td>';
rows += '<td width="1%">' + data[i].origin + '</td>';
rows += '</tr>';
}//end for
rows += '</tbody>';
rows += '</table>';
$('#documentRows').html(rows);
I was initially using a jQuery each loop but switched to the for loop which shaved off some ms.
I thought of using something like google gears to try offloading some of the processing (if that's possible in this scenario).
Any thoughts?
joinHi,
The rendering is a problem, but there is also a problem with concatenating so many strings inside the loop, especially once the string gets very large. It would probably be best to put the strings into individual elements of an array then finally use "join" to create the huge string in one fell swoop. e.g.
var r = new Array();
var j = -1, recordId;
r[++j] = '<table><thead><tr><th>ID</th><th>Status</th><th>Name</th><th>Actions</th><th>Origin</th></tr></thead><tbody>';
for (var i in data){
var d = data[i];
recordId = d.id;
r[++j] = '<tr id="';
r[++j] = recordId;
r[++j] = '" class="';
r[++j] = d.status;
r[++j] = '"><td width="1%" align="center">';
r[++j] = recordId;
r[++j] = '</td><td width="1%" align="center"><span class="status" rel="';
r[++j] = recordId;
r[++j] = '"><strong>';
r[++j] = d.status;
r[++j] = '</strong></span></td><td width="70%"><span class="name">';
r[++j] = d.name;
r[++j] = '</span></td><td width="2%"><input type="button" class="failOne" rev="';
r[++j] = recordId;
r[++j] = '" value="F"><input type="button" class="promoteOne" rev="';
r[++j] = recordId;
r[++j] = '" value="P"></td><td width="1%">';
r[++j] = d.origin;
r[++j] = '</td></tr>';
}
r[++j] = '</tbody></table>';
$('#documentRows').html(r.join(''));
Also, I would use the array indexing method shown here, rather than using "push" since, for all browsers except Google Chrome it is faster, according to this article.
Displaying that many rows is causing the browser's rendering engine to slow down, not the JavaScript engine. Unfortunately there's not a lot you can do about that.
The best solution is to just not display so many rows at the same time, either through pagination, or virtual scrolling.
The way you are building your string will cause massive amounts of garbage collection.
As the string gets longer and longer the javascript engine has to keep allocating larger buffers and discarding the old ones. Eventually it will not be able to allocate sufficient memory without recycling the remains of all the old strings.
This problem gets worse as the string grows longer.
Instead try adding new elements to the DOM one at a time using the jQuery manipulation API
Also consider only rendering what is visible and implement your own scrolling.
You can do couple of things to increase the performance:
your rows variable is getting bigger and bigger so, don't store the html in one variable. solution can be $.each() function and each function you append the element into DOM. But this is minor adjustment.
Html generating is good, but you can try DOM creating and appending. Like $('<tr></tr>').
And finally, this will solve your problem for sure : use multiple ajax call in the first ajax call collect how many data is available and fetch approximately 1,000 or may be more data. And use other calls to collect remaining data. If you want, you can use synchronous call or Asynchronous calls wisely.
But try to avoid storing the value. Your DOM size will be huge but it should work on moder browsers and forget about IE6.
#fuel37 : Example
function outputDocumentNew(data, doc_id) {
//Variable DOM's
var rowSample = $('<tr></tr>').addClass('row-class');
var colSample = $('<td></td>').addClass('col-class');
var spanSample = $('<span></span>').addClass('span-class');
var inputButtonSample = $('<input type="button"/>').addClass('input-class');
//DOM Container
var container = $('#documentRows');
container.empty().append('<table></table>');
//Static part
var head = '<thead>\
<tr>\
<th width="1%" align="center">ID</th>\
<th width="1%" align="center">Status</th>\
<th width="70%">Name</th>\
<th width="2%">Actions</th>\
<th width="1%">Origin</th>\
</tr>\
</thead>';
container.append(head);
var body = $('<tbody></tbody>');
container.append(body);
//Dynamic part
$.each(data, function (index, value) {
var _this = this;
//DOM Manupulation
var row = rowSample.clone();
//Actions
var inpFailOne = inputButtonSample.clone().val('F').attr('rev', _this.id).addClass('failOne').click(function (e) {
//do something when click the button.
});
var inpPromoteOne = inputButtonSample.clone().val('P').attr('rev', _this.id).addClass('promoteOne').click(function (e) {
//do something when click the button.
});
row
.append(colSample.clone().append(_this.id))
.append(colSample.clone().append(spanSample.colne().addClass('status').append(_this.status)))
.append(colSample.clone().append(spanSample.colne().addClass('name').append(_this.name)))
.append(colSample.clone().append(inpFailOne).append(inpPromoteOne))
.append(colSample.clone().append(_this.origin));
body.append(row);
});
}
in this process you need to create & maintain id's or classes for manipulation. You have the control to bind events and manipulate each elements there.
Answering to get formatting
What happens if you do
for(var i in data){
var record = data[i];
var recordId = record.id;
rows += '<tr id="' + recordId + '" class="' + record.status + '">';
rows += '<td width="1%" align="center">' + recordId + '</td>';
rows += '<td width="1%" align="center"><span class="status" rel="' + recordId + '"><strong>' + data[i].status + '</strong></span></td>';
rows += '<td width="70%"><span class="name">' + record.name + '</span></td>';
rows += '<td width="2%">';
rows += '<input type="button" class="failOne" rev="' + recordId + '" value="F">';
rows += '<input type="button" class="promoteOne" rev="' + recordId + '" value="P">';
rows += '</td>';
rows += '<td width="1%">' + record.origin + '</td>';
rows += '</tr>';
}//end for
Per others suggestions (I'm not reputable enough to comment yet, sorry!), you might try the TableSorter plugin to handle only displaying a usable amount of data at a time.
I don't know how it fares at very high numbers of rows, but their example data is 1000 rows or so.
This wouldn't help with JS performance but would keep the burden off the browser renderer.
Could try this...
Improve Loops
Improve String Concat
var tmpLst = [];
for (var i=0, il=data.length; i<il; i++) {
var record = data[i];
var recordId = record.id;
tmpLst.push('<tr id="');
tmpLst.push(recordId);
tmpLst.push('" class="');
tmpLst.push(record.status);
tmpLst.push('">');
tmpLst.push('<td width="1%" align="center">');
...ect...
}
rows += tmpLst.join('');
This might squeeze an extra bit of performance...
var lstReset = i * lstReset.length;
tmpLst[lstReset + 1]='<tr id="';
tmpLst[lstReset + 2]=recordId;
tmpLst[lstReset + 3]='" class="';

Categories

Resources