Summing a table column with javascript - javascript

I'm trying to use JS to sum up a column of already Javascript-generated values. I'm new to JS, so this may be way wrong. At any rate, I tried this:
NOTE -- FINAL CODE AT BOTTOM
$(".js-package").change(function(){
var parentTable = $(this).parents("table");
var table_rows = parentTable.rows;
var height = table_rows.length;
var total = 0;
for (var i = 0; i < height; i++) {
var current_row = table_rows[i];
total += current_row[5];
}
$(parentTable).find(".js-lb-total").html((total).toFixed(2));
});
This applies to a bunch of html, but the relevant stuff is that I've got this line where things are supposed to total up:
<td class="js-lb-total">?</td>
And this further up:
<td>
<%= l.select :units, dropdown, {}, :class => "unit_select js-package" %>
</td>
Importantly, the seemingly arbitrary number 5 refers to the column (assuming JS starts arrays at 0) that I'm trying to sum up.
Any idea what I'm doing wrong/how to fix it? I'll go ahead and look into opening a fiddle that I can link to with the more complete code. I'll add that link below.
Thanks!
EDIT -- Row totaling script below
$(".js-package").change(function(){
var numOfPackages = parseFloat($(this).val());
var parentTr = $(this).parents("tr");
var parentTable = $(this).parents("table");
var weight = parseFloat($(parentTr).find(".js-weight").attr('data-weight'));
var price = parseFloat($(parentTr).find(".js-lb-price").attr('data-lb-price'));
$(parentTr).find(".js-price").html(((numOfPackages * weight * price).toFixed(2)));
$(parentTr).find(".js-lbs").html((numOfPackages * weight).toFixed(2));
});
EDIT 2 -- Basic fiddle link here Fiddle. None of the JS is working there, though, for some reason. (The first bunch works on my server). So it may not be particularly helpful.
EDIT 3 -- To be clear, I'm trying to sum a column whose values are all dynamically generated by another javascript action. They're not in the html. Could that be part of the problem?
FINAL EDIT -- After much tweaking and following of advice, I got this, which works great (and totals both price and poundage, after totaling each line).
$(".js-package").change(function(){
var numOfPackages = parseFloat($(this).val());
var parentTr = $(this).parents("tr");
var parentTable = $(this).parents("table");
var weight = parseFloat($(parentTr).find(".js-weight").attr('data-weight'));
var price = parseFloat($(parentTr).find(".js-lb-price").attr('data-lb-price'));
$(parentTr).find(".js-price").html(((numOfPackages * weight * price).toFixed(2)));
$(parentTr).find(".js-lbs").html((numOfPackages * weight).toFixed(2));
var table = document.getElementById('sumtable');
var table_rows = table.rows;
var height = parseInt(table_rows.length);
var lb_total = 0;
var money_total = 0;
var cell;
for (var i = 1, iLen = height - 1; i < iLen; i++) {
cell = table_rows[i].cells[5];
lb_total += Number(cell.textContent);
}
for (var j = 1, jLen = height - 1; j < jLen; j++) {
cell = table_rows[j].cells[6];
money_total += Number(cell.textContent);
}
$(parentTable).find(".js-lb-total").html(lb_total.toFixed(2));
$(parentTable).find(".js-price-total").html(money_total.toFixed(2));
});

The example below might get you started. If the headers and footers are in a different table section, it will make life easier, I've put them all in the one tbody.
Things to note:
Values read from cells will be strings, so you need to convert them to numbers
Javascript is notoriously bad at decimal arithmetic, much better to do integer arithmetic and convert to decimal only at the very end for presentation
Be careful of Math.toFixed, it has quirks, search the questions
Good luck. :-)
<script>
// column is the column with values in it to total (first column is zero)
// Assume values are floats.
function addRows(tableId, column, resultId) {
var table = document.getElementById(tableId);
var rows = table.rows;
var total = 0;
var cell;
// Assume first row is headers, adjust as required
// Assume last row is footer, addjust as required
for (var i=1, iLen=rows.length - 1; i<iLen; i++) {
cell = rows[i].cells[column];
total += Number(cell.textContent || cell.innerText);
}
document.getElementById(resultId).innerHTML = total.toFixed(2);
}
</script>
<table id="productTable">
<tr>
<th>Item
<th>value ($)
<tr>
<td>foo
<td>23.33
<tr>
<td>bar
<td>03.04
<tr>
<td>Total
<td id="totalValue">
</table>
<button onclick="addRows('productTable', 1, 'totalValue')">Update total</button>

try this:
total = parseInt(total)+ parseInt(current_row[5].childNodes[1].value);
if that doesn't work try getting the cell of the column you want:
total =parseInt(total)+ parseInt(current_row[5].cells[try numbers here].childNodes[1].value);
hope i helped.

Related

double forEach to double for Loop

I'm creating an HTML table from a server-side array (google apps script; so tableArray is coming from there). I have two forEach functions which work. However, I'm attempting to use two for loops instead because I'd like to be able to add different classes to different <td>'s.
The output doesn't come out as expected (see #1 below). I can either get an array in one column (instead of each element of the array as a separate <td> or the arrays are repeated in each <td> (see #2 below).
What do I need to change in my for loops to get the expected output?
You can see the version that works HERE.
1 (works with forEach)
2 (does not work with for)
Index.html
function buildTable(tableArray) {
var table = document.getElementById('table');
var tableBody = document.createElement('tbody');
var tbodyID = tableBody.setAttribute('id', 'tbody');
for (var i = 0; i < tableArray.length; ++i) {
var column = tableArray[i];
var colA = column[0];
var colB = column[1];
var colC = column[2];
var colD = column[3];
if (colA != "") {
var row = document.createElement('tr');
for (var j = 0; j < column.length; ++j) {
var cell = document.createElement('td');
cell.appendChild(document.createTextNode(column));
row.appendChild(cell);
}
}
tableBody.appendChild(row);
}
table.appendChild(tableBody);
document.body.appendChild(table);
}
Instead of line cell.appendChild(document.createTextNode(column));
write cell.appendChild(document.createTextNode(column[j]));
You've forgotten to add index [j]
// Loop over rows
for (var i = 0; i < tableArr.length; i++) {
var row = tableArr[i];
// loop over columns
for( var j =0; j<row.length; j++){
//create each column and append to row
}
// append row to table body
}
// append table body to DOM
For performance reasons you want to write to the DOM only once and create the table in memory first.
Change
cell.appendChild(document.createTextNode(column));
to
cell.appendChild(document.createTextNode(column[i]));
This will make it loop through all of your column data properly instead of appending the same content of the whole array repeatedly.

Move columns of a HTML table

I want to move columns of a table around in several hundred webpages that are outside of my control so that they print nicely. I can get to the tables in question easily enough with XPATH, but rearranging them has me stumped. Essentially, I want to rearrange this:
Table:
Header1 Header2
data1 data2
To this:
Table 1:
Header1
data1
Table 2:
Header2
data2
Is this possible?
Or, a simplified view of the existing HTML:
<table><tbody>
<tr>
<th>Header1</th>
<th>Header2</th>
</tr>
<tr>
<td>data1</td>
<td>data2</td>
</tr>
</tbody></table>
It isn't strictly necessary that these become separate tables, but the data portions are just wide enough to fit on a page, so they can't be in the same row.
Thanks in advance!
Update:
I have been learning jQuery, and it looks like I could use something like this:
function jqsplit($table, chunkSize) {
var cols = $("th", $table).length;
var n = cols / chunkSize;
for (var i = 0; i <= n; i++) {
$("<br/>").appendTo("body");
var $newTable = $table.clone().appendTo("body");
for (var j = cols; j > 0; j--) {
if (j + chunkSize - 1 <= chunkSize * i || j > chunkSize * i + 1) {
$('td:nth-child(' + j + '),th:nth-child(' + j + ')', $newTable).remove();
}
}
}
}
(Stolen from here, but modified for my particular use case.)
The problem I now have is that I can't figure out how to select my initial table with jquery, and if I can process them all together. The original tables always have 2 rows and 3 columns, and no other tables match that criteria, can anyone help?
OK, Solved it. My solution is inelegant as all hell, but works perfectly. Awful code, away!
function jqsplit($table, chunkSize) {
//Splits these specific tables into 3 by creating 3 copies and deleting the unneeded rows.
var $randomP = $("<p></p>").insertAfter($table);
var $newTable1 = $table.clone().appendTo($randomP);
var $newTable2 = $table.clone().appendTo($randomP);
var $newTable3 = $table.clone().appendTo($randomP);
$newTable1.children("tbody").children("tr").children("th").get(2).remove();
$newTable1.children("tbody").children("tr").children("td").get(2).remove();
$newTable1.children("tbody").children("tr").children("th").get(1).remove();
$newTable1.children("tbody").children("tr").children("td").get(1).remove();
$newTable2.children("tbody").children("tr").children("th").get(2).remove();
$newTable2.children("tbody").children("tr").children("td").get(2).remove();
$newTable2.children("tbody").children("tr").children("th").get(0).remove();
$newTable2.children("tbody").children("tr").children("td").get(0).remove();
$newTable3.children("tbody").children("tr").children("th").get(1).remove();
$newTable3.children("tbody").children("tr").children("td").get(1).remove();
$newTable3.children("tbody").children("tr").children("th").get(0).remove();
$newTable3.children("tbody").children("tr").children("td").get(0).remove();
$table.remove();
}
//Split table
var tablelist = $("table");
$.each(tablelist, function(index,value){
if ( $(this).children("tbody").children("tr").children("th").length == 3 && $(this).children("tbody").children("tr").length == 2 && $(this).children("tbody").children("tr").children("td").length == 3) {
jqsplit($(this), 1);
//Don't want to do the below because some pages have several matching tables
//return false;
}
});

Delete rows in table built dynamically

I've been stuck on this stupid problem for a few days now. I guess it's not complicated but I might need someone's help...
Here is the thing: I start with an empty table in my html page. There is only the head with the name of the columns. Then, I get JSON data thanks to php, and I fill my table by creating a new row and new cells as follows:
for (var j = 0; j < jsonVal.length; j++){
var tr = table.insertRow(-1);
tr.insertCell(-1).innerHTML = "<img src=\"" + jsonVal[j].image + "\">";
tr.insertCell(-1).innerHTML = jsonVal[j].name;
tr.insertCell(-1).innerHTML = jsonVal[j].rating;
};
Now, all I want to do is to update this table according to some parameters (ordered by name or rating, only 5 lines etc...)
To do so, I want to delete all the rows I currently have to put with the new data. BUT I can't find a way to delete them. Here is my deleting code:
var currentTable = document.getElementsByTagName("table")[0];
var rowCount = currentTable.rows.length;
for(var i = 1; i < rowCount; i++){
currentTable.deleteRow(i);
}
I get the following error:
IndexSizeError: Index or size is negative or greater than the allowed amount
Is that the correct way to do it?
PS: sorry for such a long explanation.
My bad, I only needed to change i to 1 in my for loop as follows:
var currentTable = document.getElementsByTagName("table")[0];
var rowCount = currentTable.rows.length;
for(var i = 1; i < rowCount; i++){
currentTable.deleteRow(1);
}

Creating html table with millions record using javascript blocking browser

Please read my question complete before answer or marking it duplicate because i am dealing with constraint in my problem.
Hi All Javascript rock stars,
I am creating a html table with javascript. In table i need scroller both vertical and horizontal. I am done with this part. It is working. Now my problem starts. When i am creating millions rows for the table it block my browser. For few thousand record it work fine.
I have done lot of google to improve performance but did not gain any significant performance improvement. Like some has suggested to use row.appendChild(cell); I have also tried to use Workers. But then i found i can not have access of document object in Workers. setTimeout is also not sufficient to solve my problem. Here is my code for creating rows of table.
var arr = JSON.parse(rcvReq.responseText);
var json = arr.abc;
var columns = [];
columns = Object.keys(json[0]);
//Build an array containing Customer records.
var customers = new Array();
customers.push(columns);
for (i = 0; i < json.length; i++) {
var dataVal = [];
for(j = 0; j < columns.length; j++){
var mytempvar = json[i];
dataVal.push(mytempvar[columns[j]+'']);
}
customers.push(dataVal);
}
//Create a HTML Table element.
var table = document.createElement("TABLE");
var table1 = document.createElement("TABLE");
table.border = "1";
//Get the count of columns.
var columnCount = customers[0].length;
//Add the header row.
var row = document.createElement('tr');
row.className = 'fixed-header';
for (var i = 0; i < columnCount; i++) {
var headerCell = document.createElement("TH");
headerCell.innerHTML = customers[0][i];
row.appendChild(headerCell);
}
table1.appendChild(row);
//Add the data rows.
var documentFragment = document.createDocumentFragment();
for (var i = 1; i < customers.length; i++) {
row = document.createElement('tr');
for (var j = 0; j < columnCount; j++) {
var cellVal = customers[i][j];
var cell = document.createElement("TD");
cell.innerHTML = cellVal;
row.appendChild(cell);
}
setTimeout(function(){ }, 1000);
documentFragment.appendChild(row);
}
/* Event of button */
var siblings = e.parentNode.children;
var childOf1stShibling = siblings[0];
table.appendChild(documentFragment);
var div = document.createElement("DIV");
var dvTable = document.getElementById(siblings[1].id +'');
var dvTable1 = document.getElementById(childOf1stShibling.children[0].id + '');
dvTable.innerHTML = "";
dvTable1.innerHTML = "";
dvTable.appendChild(table);
dvTable1.appendChild(table1);
Constraint
No use of jquery. Only i can use javascript, css and html.
No use of third party plugin.
I can not use pagination.
Records are in millions not in thousand.
Please suggest any workaround keeping above constraint in mind.
Thanks so much in advance.
You can check the source of this JavaScript plugin and take the pieces you need. The whole plugin is about 300 rows of code, you can remove some parts if you do not need them. This is a link to their GitHub repo.
On their demo site they append 500k rows and the scroll is smooth.
You can not display that many DOM elements without "blocking" browser.
And there are no "optimization" techniques to handle this, the only option, as mentioned #dandavis would be to listen for scroll events and display subsets on-demand for current scroll position.
As said #Veselin Clusterize.js does exactly that, but it works vertically only. If you need to apply this technique for two directions (horizontally + vertically) - take a look at library called Fattable. Demo

Convert TD columns into TR rows

Is there a quick way to translate (using CSS or Javascript) a tables TD into TR, currently I have:
A B C D
1 2 3 4
and I want to translate to:
A 1
B 2
C 3
D 4
??
You want to turn HTML arranged like this:
<tr><td>A</td><td>B</td><td>C</td><td>D</td></tr>
<tr><td>1</td><td>2</td><td>3</td><td>4</td></tr>
Into this:
<tr><td>A</td><td>1</td></tr>
<tr><td>B</td><td>2</td></tr>
<tr><td>C</td><td>3</td></tr>
<tr><td>D</td><td>4</td></tr>
Correct?
You can do this with Javascript, however, it is difficult to suggest a method with out knowing more about the structure of your site/HTML files. I'll give it a go.
Assuming your <table> tag comes with an id (like this: <table id="myTable"> you can access it in javascript like this:
var myTable = document.getElementById('myTable');
You can create a new table like this:
var newTable = document.createElement('table');
Now you need to transpose the old tables rows into the new tables columns:
var maxColumns = 0;
// Find the max number of columns
for(var r = 0; r < myTable.rows.length; r++) {
if(myTable.rows[r].cells.length > maxColumns) {
maxColumns = myTable.rows[r].cells.length;
}
}
for(var c = 0; c < maxColumns; c++) {
newTable.insertRow(c);
for(var r = 0; r < myTable.rows.length; r++) {
if(myTable.rows[r].length <= c) {
newTable.rows[c].insertCell(r);
newTable.rows[c].cells[r] = '-';
}
else {
newTable.rows[c].insertCell(r);
newTable.rows[c].cells[r] = myTable.rows[r].cells[c].innerHTML;
}
}
}
This ought to do what you need. Be forewarned: not tested. Working this javascript code into an HTML page is left as an exercise for the reader. If anyone spots any errors that I missed, I be gratified if you point them out to me or simply edit to fix :)
Here is a tested function that will transpose a table, and it will preserve any formatting/events you had hooked to any elements within the table (ie: onclicks on cell or cell contents)
function TransposeTable(tableId)
{
var tbl = $('#' + tableId);
var tbody = tbl.find('tbody');
var oldWidth = tbody.find('tr:first td').length;
var oldHeight = tbody.find('tr').length;
var newWidth = oldHeight;
var newHeight = oldWidth;
var jqOldCells = tbody.find('td');
var newTbody = $("<tbody></tbody>");
for(var y=0; y<newHeight; y++)
{
var newRow = $("<tr></tr>");
for(var x=0; x<newWidth; x++)
{
newRow.append(jqOldCells.eq((oldWidth*x)+y));
}
newTbody.append(newRow);
}
tbody.replaceWith(newTbody);
}
Notes:
- Requires Jquery
- Not tested on very large tables
- Will likely crap out on tables with spanned columns/rows
- Will likely crap out on tables with any combination of thead/th
So as long as your tables have no spanned cells and doesnt use thead/th, should be good to go.
This is a solution, in this case this was for for mobiles and tablets, remove the media queries and the th position absolute and u have it!
based on this:
table, thead, tbody, th, td, tr {
display: block;
}
http://css-tricks.com/responsive-data-tables/
Maybe someone needs it: I converted TR to TD by CSS (a table with one column).
Apply from second line until end of the table on TR the code below:
<tr style="float:left;">
Also used the code below to show each 3 rows as one line of 3 columns:
<tr style="float:left; width:33.33%;">

Categories

Resources