I am trying to add Price from table column to a total.
I am having problem adding values such as 10.00 or 5.99. I am able to calculate prices with int values, but not with values 10.00 or 5.99, etc.
Here is what I have below.
var table = document.getElementById("myTable"),
sumVal = 0;
for (var i = 1; i < table.rows.length; i++) {
sumVal = sumVal + parseF(table.rows[i].cells[2].innerHTML);
}
document.getElementById("val").innerHTML = "SubTotal =" + sumVal;
console.log(sumVal);
<table id="myTable">
<tr>
<th>Item</th>
<th>Price</th>
<th>Remove</th>
</tr>
<tr>
<td>Hoddie</td>
<td class="count-me">15.00</td>
<td><button onClick="myFunction()">Remove</button></td>
</tr>
<tr>
<td>Nike Cap</td>
<td class="count-me">10.99</td>
<td><button onClick="myFunction()">Remove</button></td>
</tr>
</table>
<span id="val"></span>
You have three issues:
You are grabbing the wrong cell index, indices start at 0:
table.rows[i].cells[1]
You need to call the correct parse function:
parseFloat(table.rows[i].cells[1].innerHTML);
You need to format your output:
"SubTotal = $" + sumVal.toFixed(2);
Update: Added functionality for removing rows.
updateSubTotal(); // Initial call
function updateSubTotal() {
var table = document.getElementById("myTable");
let subTotal = Array.from(table.rows).slice(1).reduce((total, row) => {
return total + parseFloat(row.cells[1].innerHTML);
}, 0);
document.getElementById("val").innerHTML = "SubTotal = $" + subTotal.toFixed(2);
}
function onClickRemove(deleteButton) {
let row = deleteButton.parentElement.parentElement;
row.parentNode.removeChild(row);
updateSubTotal(); // Call after delete
}
#myTable td {
padding: 0.25em;
}
#val {
display: block;
margin-top: 0.5em;
}
<table id="myTable">
<tr>
<th>Item</th>
<th>Price</th>
<th>Remove</th>
</tr>
<tr>
<td>Hoodie</td>
<td class="count-me">15.00</td>
<td><button onClick="onClickRemove(this)">Remove</button></td>
</tr>
<tr>
<td>Nike Cap</td>
<td class="count-me">10.99</td>
<td><button onClick="onClickRemove(this)">Remove</button></td>
</tr>
</table>
<span id="val"></span>
You are accessing the incorrect array element and also need to use parseFloat
The cells array is zero-based so you need to use cells[1] to access the second column:
var table = document.getElementById("myTable"),
sumVal = 0;
for (var i = 1; i < table.rows.length; i++) {
sumVal = sumVal + parseFloat(table.rows[i].cells[1].innerHTML);
}
document.getElementById("val").innerHTML = "SubTotal =" + sumVal;
console.log(sumVal);
<table id="myTable">
<tr>
<th>Item</th>
<th>Price</th>
<th>Remove</th>
</tr>
<tr>
<td>Hoddie</td>
<td class="count-me">15.00</td>
<td><button onClick="myFunction()">Remove</button></td>
</tr>
<tr>
<td>Nike Cap</td>
<td class="count-me">10.99</td>
<td><button onClick="myFunction()">Remove</button></td>
</tr>
</table>
<span id="val"></span>
updateSubTotal(); // Initial call
function updateSubTotal() {
var table = document.getElementById("myTable");
let subTotal = Array.from(table.rows).slice(1).reduce((total, row) => {
return total + parseFloat(row.cells[1].innerHTML);
}, 0);
let subTotal2 = Array.from(table.rows).slice(1).reduce((total, row) => {
return total + parseFloat(row.cells[2].innerHTML);
}, 0);
document.getElementById("val").innerHTML = "SubTotal = $" + subTotal.toFixed(2);
document.getElementById("val1").innerHTML = subTotal2.toFixed(2);
}
function onClickRemove(deleteButton) {
let row = deleteButton.parentElement.parentElement;
row.parentNode.removeChild(row);
updateSubTotal(); // Call after delete
}
#myTable td {
padding: 0.25em;
}
#val {
display: block;
margin-top: 0.5em;
}
<table id="myTable">
<tr>
<th>Item</th>
<th>Price</th>
<th>M2</th>
<th>Remove</th>
</tr>
<tr>
<td>Hoodie</td>
<td class="count-me">15.00</td>
<td class="count-me">34.00</th>
<td><button onClick="onClickRemove(this)">Remove</button></td>
</tr>
<tr>
<td>Nike Cap</td>
<td class="count-me">10.99</td>
<td class="count-me">22.34</th>
<td><button onClick="onClickRemove(this)">Remove</button></td>
</tr>
</table>
<span id="val"></span>
<span id="val1"></span>
var cell = document.getElementsByClassName("count-me");
var val = 0;
var i = 0;
while (cell[i] != undefined) {
val += parseFloat(cell[i].innerHTML);
i++;
} //end while
document.getElementById("val").innerHTML = parseFloat(val).toFixed(2);
console.log(parseFloat(val).toFixed(2));
<table id="myTable">
<tr>
<th>Item</th>
<th>Price</th>
<th>Remove</th>
</tr>
<tr id="">
<td>Hoddie</td>
<td class="count-me">15.00</td>
<td>
<button onClick="myFunction()">Remove</button>
</td>
</tr>
<tr>
<td>Nike Cap</td>
<td class="count-me">10.99</td>
<td>
<button onClick="myFunction()">Remove</button>
</td>
</tr>
</table>
<span id="val"></span>
In my Django project I have a table which datas coming from database. There can be hundreds of rows. I can't display all in one page. I want to display my tables 10 rows for each. I got two buttons for next and previous rows. Can I do this with jquery or some python code?
index.html
<table class="table" border=1>
<thead>
<tr>
<th>Full Name</th>
<th>Company</th>
<th>Email</th>
<th>Phone Number</th>
<th>Note</th>
<th>ID</th>
<th>Item Barcode</th>
</tr>
</thead>
<tbody>
{% for x in thelist %}
<tr>
<td>{{x.full_name}}</td>
<td>{{x.company}}</td>
<td>{{x.email}}</td>
<td>{{x.phone_number}}</td>
<td>{{x.note}}</td>
<td>{{x.id}}</td>
<td>{{x.item_barcode}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="button" class="btn btn-primary" name="button">Next</button>
<button type="button" class="btn btn-primary" name="button">Previous</button>
You can use bootstart datatables for this:
<link rel="stylesheet" href="https://cdn.datatables.net/1.12.1/css/jquery.dataTables.min.css">
<table class="table" border=1 id='mydatatable'>
<thead>
<tr>
<th>Full Name</th>
<th>Company</th>
<th>Email</th>
<th>Phone Number</th>
<th>Note</th>
<th>ID</th>
<th>Item Barcode</th>
</tr>
</thead>
<tbody>
{% for x in thelist %}
<tr>
<td>{{x.full_name}}</td>
<td>{{x.company}}</td>
<td>{{x.email}}</td>
<td>{{x.phone_number}}</td>
<td>{{x.note}}</td>
<td>{{x.id}}</td>
<td>{{x.item_barcode}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<script src='https://cdn.datatables.net/1.12.1/js/jquery.dataTables.min.js'></script>
<script>
$(document).ready(function () {
$('#mydatatable').DataTable();
});
</script>
For More information you can refer https://datatables.net/
You could use the rowIndex and two classes (for example .active and .visible) for the first visible row and the styling via CSS:
If there is no class .active add it to the first row:
if (!document.querySelector('.active')) {
table_rows[0].classList.add('active');
}
In the next step (at best in a separate function, for example showRows()) remove the class .visible from all rows and then add it to so many rows like you defined with the constant visible_rows, beginning with the active row:
function showRows() {
let active_row = document.querySelector('.active');
for (i = 0; i < table_rows.length; i++) {
table_rows[i].classList.remove('visible');
}
for (i = 0; i < visible_rows; i++) {
active_row.classList.add('visible');
if (active_row.nextElementSibling) {
active_row = active_row.nextElementSibling;
}
}
}
In the event handlers for #next and #previous just move the class .active by the number of visible rows, if there are rows left in that direction, and call the function showRows():
document.querySelector('#next').addEventListener('click', function() {
const active_row = document.querySelector('.active');
const active_index = active_row.rowIndex;
if (table_rows.length > active_index + visible_rows - 1) {
active_row.classList.remove('active');
table_rows[active_index + visible_rows - 1].classList.add('active');
showRows();
}
});
document.querySelector('#prev').addEventListener('click', function() {
const active_row = document.querySelector('.active');
const active_index = active_row.rowIndex;
if (active_index > 1) {
active_row.classList.remove('active');
table_rows[active_index - visible_rows - 1].classList.add('active');
showRows();
}
});
Working example:
const visible_rows = 2;
const table_rows = document.querySelectorAll('tbody tr');
if (!document.querySelector('.active')) {
table_rows[0].classList.add('active');
}
function showRows() {
let active_row = document.querySelector('.active');
for (i = 0; i < table_rows.length; i++) {
table_rows[i].classList.remove('visible');
}
for (i = 0; i < visible_rows; i++) {
active_row.classList.add('visible');
if (active_row.nextElementSibling) {
active_row = active_row.nextElementSibling;
}
}
}
document.querySelector('#next').addEventListener('click', function() {
const active_row = document.querySelector('.active');
const active_index = active_row.rowIndex;
if (table_rows.length > active_index + visible_rows - 1) {
active_row.classList.remove('active');
table_rows[active_index + visible_rows - 1].classList.add('active');
showRows();
}
});
document.querySelector('#prev').addEventListener('click', function() {
const active_row = document.querySelector('.active');
const active_index = active_row.rowIndex;
if (active_index > 1) {
active_row.classList.remove('active');
table_rows[active_index - visible_rows - 1].classList.add('active');
showRows();
}
});
showRows();
tbody tr:not(.visible) {
display: none;
}
<table class="table" border=1>
<thead>
<tr>
<th>Full Name</th>
<th>Company</th>
<th>Email</th>
<th>Phone Number</th>
<th>Note</th>
<th>ID</th>
<th>Item Barcode</th>
</tr>
</thead>
<tbody>
<tr>
<td>A</td>
<td>B</td>
<td>C</td>
<td>1</td>
<td>D</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td>E</td>
<td>F</td>
<td>G</td>
<td>4</td>
<td>H</td>
<td>5</td>
<td>6</td>
</tr>
<tr>
<td>I</td>
<td>J</td>
<td>K</td>
<td>7</td>
<td>L</td>
<td>8</td>
<td>9</td>
</tr>
</tbody>
</table>
<button id="next" type="button" class="btn btn-primary" name="button">Next</button>
<button id="prev" type="button" class="btn btn-primary" name="button">Previous</button>
I am facing a problem with a functionality of moving a table row up or down. In my code the rows are moving up and down perfectly but in that I don't want to change a particular column value. In my case it will be the right most column before the button columns. Please have a look of my existing code and help me solve.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
table.myTable {
counter-reset: rowNumber;
}
table.myTable tr {
counter-increment: rowNumber;
}
table.myTable tr td:first-child::before {
content: counter(rowNumber);
min-width: 1em;
margin-right: 0.5em;
}
button.uparrow{
background-color:#FFCC66;
}
button.downarrow{
background-color:#66FF66;
}
</style>
<script>
function get_previoussibling(n)
{
x=n.previousSibling;
while (x.nodeType!=1)
{
x=x.previousSibling;
}
return x;
}
function get_nextsibling(n)
{
x=n.nextSibling;
while ( x != null && x.nodeType!=1)
{
x=x.nextSibling;
}
return x;
}
function MoveUp()
{
var table,
row = this.parentNode;
while ( row != null ) {
if ( row.nodeName == 'TR' ) {
break;
}
row = row.parentNode;
}
table = row.parentNode;
table.insertBefore ( row, get_previoussibling( row ) );
}
function MoveDown()
{
var table,
row = this.parentNode;
while ( row != null ) {
if ( row.nodeName == 'TR' ) {
break;
}
row = row.parentNode;
}
table = row.parentNode;
table.insertBefore ( row, get_nextsibling ( get_nextsibling( row ) ) );
}
</script>
<title>Moving Row Up/Down</title>
</head>
<body>
<table class="myTable" border="1" width="80%">
<tr >
<td></td>
<td>Justine</td>
<td>Male</td>
<td>10</td>
<td style="width:30px;"><button class="uparrow" onClick="MoveUp.call(this);">⇧</button></td>
<td style="width:30px;"><button class="downarrow" onClick="MoveDown.call(this);">⇩</button></td>
</tr>
<tr>
<td></td>
<td>Michael</td>
<td>Male</td>
<td>21</td>
<td><button class="uparrow" onClick="MoveUp.call(this);">⇧</button></td>
<td><button class="downarrow" onClick="MoveDown.call(this);">⇩</button></td>
</tr>
<tr>
<td></td>
<td>Robert</td>
<td>Male</td>
<td>24</td>
<td><button class="uparrow" onClick="MoveUp.call(this);">⇧</button></td>
<td><button class="downarrow" onClick="MoveDown.call(this);">⇩</button></td>
</tr>
<tr>
<td></td>
<td>Samuel</td>
<td>Male</td>
<td>30</td>
<td><button class="uparrow" onClick="MoveUp.call(this);">⇧</button></td>
<td><button class="downarrow" onClick="MoveDown.call(this);">⇩</button></td>
</tr>
<tr>
<td></td>
<td>Clifa</td>
<td>Female</td>
<td>34</td>
<td><button class="uparrow" onClick="MoveUp.call(this);">⇧</button></td>
<td><button class="downarrow" onClick="MoveDown.call(this);">⇩</button></td>
</tr>
</table>
</body>
</html>
e.g. if I am click on the up button or down button of 3rd row, In my case entire row is interchanging position with either previous row or next row for up and down respectively. Whereas for up button 21 should not change the place with 24 and for down 30 should not change the place with 24.
Please help.
One way of doing this while following your current pattern is to swap the text content of the target elements when switching.
Quick Example: https://codepen.io/davidatthepark/pen/NvVEaP?editors=1010
function get_previoussibling(n) {
x = n.previousSibling;
while (x.nodeType != 1) {
x = x.previousSibling;
}
return x;
}
function get_nextsibling(n) {
x = n.nextSibling;
while (x != null && x.nodeType != 1) {
x = x.nextSibling;
}
return x;
}
function MoveUp() {
var table,
row = this.parentNode,
rowText,
previousSibling;
while (row != null) {
if (row.nodeName == "TR") {
break;
}
row = row.parentNode;
}
table = row.parentNode;
previousSibling = get_previoussibling(row);
rowText = row.children[3].textContent;
row.children[3].textContent = previousSibling.children[3].textContent;
previousSibling.children[3].textContent = rowText;
table.insertBefore(row, previousSibling);
}
function MoveDown() {
var table,
row = this.parentNode,
rowText,
nextSibling;
while (row != null) {
if (row.nodeName == "TR") {
break;
}
row = row.parentNode;
}
table = row.parentNode;
nextSibling = get_nextsibling(row);
rowText = row.children[3].textContent;
row.children[3].textContent = nextSibling.children[3].textContent;
nextSibling.children[3].textContent = rowText;
table.insertBefore(row, get_nextsibling(get_nextsibling(row)));
}
table.myTable {
counter-reset: rowNumber;
}
table.myTable tr {
counter-increment: rowNumber;
}
table.myTable tr td:first-child::before {
content: counter(rowNumber);
min-width: 1em;
margin-right: 0.5em;
}
button.uparrow{
background-color:#FFCC66;
}
button.downarrow{
background-color:#66FF66;
}
<table class="myTable" border="1" width="80%">
<tr>
<td></td>
<td>Justine</td>
<td>Male</td>
<td>10</td>
<td style="width:30px;"><button class="uparrow" onClick="MoveUp.call(this);">⇧</button></td>
<td style="width:30px;"><button class="downarrow" onClick="MoveDown.call(this);">⇩</button></td>
</tr>
<tr>
<td></td>
<td>Michael</td>
<td>Male</td>
<td>21</td>
<td><button class="uparrow" onClick="MoveUp.call(this);">⇧</button></td>
<td><button class="downarrow" onClick="MoveDown.call(this);">⇩</button></td>
</tr>
<tr>
<td></td>
<td>Robert</td>
<td>Male</td>
<td>24</td>
<td><button class="uparrow" onClick="MoveUp.call(this);">⇧</button></td>
<td><button class="downarrow" onClick="MoveDown.call(this);">⇩</button></td>
</tr>
<tr>
<td></td>
<td>Samuel</td>
<td>Male</td>
<td>30</td>
<td><button class="uparrow" onClick="MoveUp.call(this);">⇧</button></td>
<td><button class="downarrow" onClick="MoveDown.call(this);">⇩</button></td>
</tr>
<tr>
<td></td>
<td>Clifa</td>
<td>Female</td>
<td>34</td>
<td><button class="uparrow" onClick="MoveUp.call(this);">⇧</button></td>
<td><button class="downarrow" onClick="MoveDown.call(this);">⇩</button></td>
</tr>
</table>
I'm trying to write an efficient javascript function that will loop through a table, grab all numbers, and ignore all tds with strings. The columns will be added and averaged, and rows will be appended for each.
I have the basic functionality for this working. Whereas, if the table does not include a string, the results are as expected. When the table does include a string, the total and average of the column are way off and I'm not exactly sure how the answer is being calculated. I'm hoping somebody can help me figure out a way to ignore these values all together, and create a more efficient way of writing this function.
Finally, I want to be able to call this function by simply passing in the table, e.g. buildRows(table);
Here's what I got so far:
// function to build total and average rows
function buildRow($element) {
var result = [];
$($element).find('tbody tr').each(function() {
// Ignore the first column reserved for labels
$('td:not(:first)', this).each(function(index, val) {
if (!result[index]) result[index] = 0;
result[index] += parseInt($(val).text());
});
});
// Get the total amount rows
var rowCount = $($element).find('tbody tr').length;
// Add Average Row
$($element).append('<tr class="avg-row"></tr>');
$($element).find('tr').last().append('<td>' + 'Averages' + '</td>');
$(result).each(function() {
$($element).find('tr').last().append('<td class="avg-td">' + this / rowCount + '</td>');
});
// Add Total Row
$($element).append('<tr class="total-row"></tr>');
$($element).find('tr').last().append('<td>' + 'Totals' + '</td>');
$(result).each(function() {
$($element).find('tr').last().append('<td class="total-td">' + this + '</td>');
});
}
// ideal function calls
var tableOne = $('.tableOne');
buildRow(tableOne);
var tableTwo = $('.tableTwo');
buildRow(tableTwo);
table {
border: 1px solid #333;
padding: 10px;
margin-right: 5px;
}
table tr:nth-child(odd) {
background-color: #e0e0e0;
}
table td {
padding: 10px;
border: 1px solid #555;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="tableOne">
<tr>
<td>Row One</td>
<td>45</td>
<td>23.356</td>
<td>88</td>
</tr>
<tr>
<td>Row Two</td>
<td>111440.568</td>
<td>115555</td>
<td>4.21598</td>
</tr>
<tr>
<td>Row Three</td>
<td>27</td>
<td>42</td>
<td>144487.11</td>
</tr>
<tr>
<td>Row Four</td>
<td>23.356</td>
<td>125%</td>
<td>778978523.36</td>
</tr>
</table>
<table class="tableTwo">
<tr>
<td>Row One</td>
<td>45</td>
<td>23.356</td>
<td>Hello</td>
</tr>
<tr>
<td>Row Two</td>
<td>111440.568</td>
<td>115555</td>
<td>4.21598</td>
</tr>
<tr>
<td>Row Three</td>
<td>Dog</td>
<td>true</td>
<td>144487.11</td>
</tr>
<tr>
<td>Row Four</td>
<td>23.356</td>
<td>125%</td>
<td>778978523.36</td>
</tr>
</table>
The first table with no strings seems okay, 2nd table the results are way off and I'm not sure how the totals are being calculated.
You cannot just do parseInt on every value, it sometimes transform a string to a weird number. You have to check if it is not a valid number before you sum it up. I think of a regex or something like that, untested.
var v = $(val).text();
if(v.match(/^[\s0-9\.]+$/)){}
You need to check whether td value has integer or not by using var reg = /^\d+$/;
Since your using jQuery why not just use jQuery's built in is numeric condition check function $.isNumeric which will parse out pretty much all the data you don't want.
// true (numeric)
$.isNumeric( "-10" )
$.isNumeric( "0" )
$.isNumeric( 0xFF )
$.isNumeric( "0xFF" )
$.isNumeric( "8e5" )
$.isNumeric( "3.1415" )
$.isNumeric( +10 )
$.isNumeric( 0144 )
// false (non-numeric)
$.isNumeric( "-0x42" )
$.isNumeric( "7.2acdgs" )
$.isNumeric( "" )
$.isNumeric( {} )
$.isNumeric( NaN )
$.isNumeric( null )
$.isNumeric( true )
$.isNumeric( Infinity )
$.isNumeric( undefined )
Source: https://api.jquery.com/jQuery.isNumeric/
Example: https://jsfiddle.net/1d9Lbnp1/1/
You could just ignore the Nans and use Number instead of parseInt if you want something more accurate:
$('td:not(:first)', this).each(function(index, val) {
if (!result[index]) result[index] = 0;
var num = Number($(val).text());
num = isNaN(num) ? 0 : num;
result[index] += num;
});
Two things to remember:
parseFloat will capture floats and integers
When working with numbers, you can use the ||0 to hack ignore the falsy values, replacing them with a 0
Other than that, I took some liberties with your code, putting the necessary elements in variables:
// function to build total and average rows
function buildRow($table) {
var result = [],
$tbody = $table.find('tbody'),
$rows = $tbody.find('tr'),
row_count = $rows.length;
$rows.each(function() {
// Ignore the first column reserved for labels
var $cells = $('td:not(:first)',this);
$cells.each(function(index,cell) {
if (!result[index])
result[index] = 0;
result[index] += parseFloat($(cell).text()||0);
});
});
// Add Average Row
var $avg_row = $('<tr class="avg-row"></tr>');
$avg_row.append('<td>Averages</td>');
$.each(result,function() {
$avg_row.append('<td class="avg-td">' + ( this / row_count ) + '</td>');
});
$tbody.append($avg_row);
// Add Total Row
var $total_row = $('<tr class="total-row"></tr>');
$total_row.append('<td>Totals</td>');
$.each(result,function() {
$total_row.append('<td class="total-td">' + this + '</td>');
});
$tbody.append($total_row);
}
// ideal function calls
var $tableOne = $('.tableOne');
buildRow($tableOne);
var $tableTwo = $('.tableTwo');
buildRow($tableTwo);
table {
border: 1px solid #333;
padding: 10px;
margin-right: 5px;
}
table tr:nth-child(odd) {
background-color: #e0e0e0;
}
table td {
padding: 10px;
border: 1px solid #555;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="tableOne">
<tr>
<td>Row One</td>
<td>45</td>
<td>23.356</td>
<td>88</td>
</tr>
<tr>
<td>Row Two</td>
<td>111440.568</td>
<td>115555</td>
<td>4.21598</td>
</tr>
<tr>
<td>Row Three</td>
<td>27</td>
<td>42</td>
<td>144487.11</td>
</tr>
<tr>
<td>Row Four</td>
<td>23.356</td>
<td>125%</td>
<td>778978523.36</td>
</tr>
</table>
<table class="tableTwo">
<tr>
<td>Row One</td>
<td>45</td>
<td>23.356</td>
<td>Hello</td>
</tr>
<tr>
<td>Row Two</td>
<td>111440.568</td>
<td>115555</td>
<td>4.21598</td>
</tr>
<tr>
<td>Row Three</td>
<td>Dog</td>
<td>true</td>
<td>144487.11</td>
</tr>
<tr>
<td>Row Four</td>
<td>23.356</td>
<td>125%</td>
<td>778978523.36</td>
</tr>
</table>
Here's another way to do the same thing, by retrieving the values first by column (using nth-child) and then using reduce to calculate sums:
['.tableOne', '.tableTwo'].forEach(function(tbl){
makeAggregates($(tbl));
});
function makeAggregates($tbl) {
var $tbody = $tbl.find('tbody');
var sums = sumOfColumns($tbody);
var $total_row = $('<tr class="total-row"><td class="total-td">Total</td></tr>');
var $average_row = $('<tr class="avg-row"><td class="avg-td">Averages</td></tr>');
$.each(sums, function(key,col) {
var total = col[1],
items = col[0];
$total_row.append('<td class="total-row">' + total + '</td>');
$average_row.append('<td class="avg-row">' + total / items + '</td>');
});
$tbody.append($average_row,$total_row);
}
function sumOfColumns($tbody) {
var $rows = $tbody.find('tr');
// Get number of columns
var num_cols = $rows.first().find('td').length;
var col_sums = {};
for (var col = 2; col < num_cols + 1; col++) {
var $col_data = $tbody.find('td:nth-child(' + col + ')'),
arr_values = $col_data.map(function() {
return this.textContent
}).get();
col_sums[col - 1] = [
arr_values.length,
arr_values.reduce(function(pv, cv) {
return pv + (parseFloat(cv) || 0)
}, 0)
];
}
return col_sums;
}
table {
border: 1px solid #333;
padding: 10px;
margin-right: 5px;
}
table tr:nth-child(odd) {
background-color: #e0e0e0;
}
table td {
padding: 10px;
border: 1px solid #555;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="tableOne">
<tr>
<td>Row One</td>
<td>45</td>
<td>23.356</td>
<td>88</td>
</tr>
<tr>
<td>Row Two</td>
<td>111440.568</td>
<td>115555</td>
<td>4.21598</td>
</tr>
<tr>
<td>Row Three</td>
<td>27</td>
<td>42</td>
<td>144487.11</td>
</tr>
<tr>
<td>Row Four</td>
<td>23.356</td>
<td>125%</td>
<td>778978523.36</td>
</tr>
</table>
<table class="tableTwo">
<tr>
<td>Row One</td>
<td>45</td>
<td>23.356</td>
<td>Hello</td>
</tr>
<tr>
<td>Row Two</td>
<td>111440.568</td>
<td>115555</td>
<td>4.21598</td>
</tr>
<tr>
<td>Row Three</td>
<td>Dog</td>
<td>true</td>
<td>144487.11</td>
</tr>
<tr>
<td>Row Four</td>
<td>23.356</td>
<td>125%</td>
<td>778978523.36</td>
</tr>
</table>
As others have mentioned, the trick is to check if the value is a number before using it in your calculations. That's as easy as isNaN(parseInt(string)).
Overall I think you can simplify your code a lot by extracting the (good) data from the table into arrays of numbers (one array for each column). You can see that in the getData function in the below snippet. Then it's a simple matter of iterating over each column's data to calculate its total and average.
You can also simplify things by using semantic markup: <tbody> for your data, <th> for heading cells, and <tfoot> for the Averages and Totals rows. This makes both extracting the data and styling the output much easier.
Here's how it looks with jQuery. Mind you, my jQuery's a bit rusty. At the end of the post you'll find another snippet sans jQuery, which is only slightly more verbose.
jQuery
function getData($rows) {
return $('td', $rows[0]).toArray().map((_, idx) =>
$rows.toArray().reduce((result, row) => {
const value = parseInt($('td', row)[idx].innerText, 10);
return result.concat(isNaN(value) ? [] : value);
}, [])
);
}
function generateRow(label, values, precision=3) {
const mult = Math.pow(10, precision);
return $(`<tr><th>${label}</th></tr>`).append(
values.map(val => $(`<td>${Math.round(val * mult) / mult}</td>`)));
}
function aggregateData($table) {
const averages = [];
const totals = [];
getData($table.find('tbody tr')).forEach(values => {
const total = values.reduce((total, val) => (total + val), 0);
totals.push(total);
averages.push(total / values.length);
});
$('<tfoot>').append(
generateRow('Averages', averages),
generateRow('Totals', totals)
).appendTo($table)
}
aggregateData($('.tableOne'));
aggregateData($('.tableTwo'));
table { width: 300px; margin-bottom: 1em; border: 1px solid gray; }
tbody tr:nth-child(odd) { background-color: #e0e0e0; }
td, th { padding: 7px; text-align: center; }
tfoot tr { background-color: paleturquoise; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="tableOne">
<tbody>
<tr>
<th>One</th>
<td>10</td>
<td>300</td>
<td>6000</td>
</tr>
<tr>
<th>Two</th>
<td>20</td>
<td>400</td>
<td>7000</td>
</tr>
<tr>
<th>Three</th>
<td>30</td>
<td>500</td>
<td>8000</td>
</tr>
<tr>
<th>Four</th>
<td>40</td>
<td>600</td>
<td>9000</td>
</tr>
</tbody>
</table>
<table class="tableTwo">
<tr>
<th>One</th>
<td>10</td>
<td>300</td>
<td>Hello</td>
</tr>
<tr>
<th>Two</th>
<td>20</td>
<td>400</td>
<td>7000</td>
</tr>
<tr>
<th>Three</th>
<td>Dog</td>
<td>true</td>
<td>8000</td>
</tr>
<tr>
<th>Four</th>
<td>40</td>
<td>600</td>
<td>9000</td>
</tr>
</table>
No jQuery
function getData(rows) {
const numCols = rows[0].querySelectorAll('td').length;
return new Array(numCols).fill().map((_, idx) =>
Array.prototype.reduce.call(rows, (result, row) => {
const value = parseInt(row.querySelectorAll('td')[idx].innerText, 10);
return result.concat(isNaN(value) ? [] : value);
}, [])
);
}
function generateRow(label, values, precision=3) {
const mult = Math.pow(10, precision);
const row = document.createElement('tr');
row.innerHTML = values.reduce((html, val) => html + `<td>${Math.round(val * mult) / mult}</td>`, `<th>${label}</th>`);
return row;
}
function aggregateData(table) {
const totals = [];
const averages = [];
getData(table.querySelectorAll('tbody tr')).forEach(values => {
const total = values.reduce((total, val) => (total + val), 0);
totals.push(total);
averages.push(total / values.length);
});
const tfoot = document.createElement('tfoot');
tfoot.appendChild(generateRow('Averages', averages));
tfoot.appendChild(generateRow('Totals', totals));;
table.appendChild(tfoot);
}
aggregateData(document.querySelector('.tableOne'));
aggregateData(document.querySelector('.tableTwo'));
table { width: 300px; margin-bottom: 1em; border: 1px solid gray; }
tbody tr:nth-child(odd) { background-color: #e0e0e0; }
td, th { padding: 7px; text-align: center; }
tfoot tr { background-color: paleturquoise; }
<table class="tableOne">
<tbody>
<tr>
<th>One</th>
<td>10</td>
<td>300</td>
<td>6000</td>
</tr>
<tr>
<th>Two</th>
<td>20</td>
<td>400</td>
<td>7000</td>
</tr>
<tr>
<th>Three</th>
<td>30</td>
<td>500</td>
<td>8000</td>
</tr>
<tr>
<th>Four</th>
<td>40</td>
<td>600</td>
<td>9000</td>
</tr>
</tbody>
</table>
<table class="tableTwo">
<tr>
<th>One</th>
<td>10</td>
<td>300</td>
<td>Hello</td>
</tr>
<tr>
<th>Two</th>
<td>20</td>
<td>400</td>
<td>7000</td>
</tr>
<tr>
<th>Three</th>
<td>Dog</td>
<td>true</td>
<td>8000</td>
</tr>
<tr>
<th>Four</th>
<td>40</td>
<td>600</td>
<td>9000</td>
</tr>
</table>
I am trying to identify as to what row number the user clicks on and to alert them. I wanted to exempt the table headers from being counted so I have intentionally used the this.rowIndex - 1 but when I click on any row, the alert box is returning a NaN value.
How do you fix this?
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
#mstrTable {
border: 1px solid black
}
#mstrTable td, th {
border: 1px solid black
}
#mstrTable tr.normal td {
color: black;
background-color: white;
}
#mstrTable tr.highlighted td {
color: white;
background-color: gray;
}
</style>
</head>
<body>
<table id="mstrTable">
<thead>
<tr>
<th>File Number</th>
<th>Date1</th>
<th>Date2</th>
<th>Status</th>
<th>Num.</th>
</tr>
</thead>
<tbody>
<tr>
<td>KABC</td>
<td>09/12/2002</td>
<td>09/12/2002</td>
<td>Submitted</td>
<td>0</td>
</tr>
<tr>
<td>KCBS</td>
<td>09/11/2002</td>
<td>09/11/2002</td>
<td>Approved</td>
<td>1 </td>
</tr>
<tr>
<td>WFLA</td>
<td>09/11/2002</td>
<td>09/11/2002</td>
<td>Submitted</td>
<td>2</td>
</tr>
<tr>
<td>WTSP</td>
<td>09/15/2002</td>
<td>09/15/2002</td>
<td>In-Progress</td>
<td>3</td>
</tr>
</tbody>
</table>
<script type="text/javascript">
(
function( )
{
var trows = document.getElementById("mstrTable").rows;
for ( var t = 1; t < trows.length; ++t )
{
trow = trows[t];
trow.className = "normal";
trow.onclick = highlightRow;
}
function highlightRow(e)
{
alert('Row is ' + this.rowIndex-1)
for ( var t = 1; t < trows.length; ++t )
{
trow = trows[t];
trow.className = ( trow == this && trow.className != "highlighted") ? "highlighted" : "normal";
}
}
}
)();
</script>
</body>
</html>
Put parentheses around math operations so they take precedence over string concatenation.
alert('Row is ' + (this.rowIndex-1))
Fiddle
Change it to this.rowIndex instead of this.rowIndex-1.