With JavaScript, how can I add a new row to an HTML table that sums the values following the 2nd row.
Here's an example of what the above depicts:
Expected outcome:
Here is the html markup:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<!DOCTYPE html>
<html>
<body>
<table border="1" style="width:100%">
<tr>
<td>Branch</td>
<td>Division</td>
<td>TallyA</td>
<td>TallyB</td>
<td>TallyC</td>
</tr>
<tr>
<td>Alpha</td>
<td>A101</td>
<td>6</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td>Bravo</td>
<td>B102</td>
<td>16</td>
<td>8</td>
<td>10</td>
</tr>
<tr>
<td>Charlie</td>
<td>C103</td>
<td>11</td>
<td>17</td>
<td>21</td>
</tr>
</table>
</body>
</html>
</body>
</html>
Here is my solution. It may look verbose, but that's necessary with the complexity of this task.
First, I referenced the button and table elements. To prevent repeated clicks that will add more rows, and destroy the code, I created a boolean variable that checks whether the rows have already been summed. The code only runs the first time inside the if(!summedItems) conditional.
I then created new td elements for all five columns in the table and created text nodes for all but the division one.
I then created selectors and this is where it got tricky. The first row does not contain numerical value, so I had to query all rows from the second further. I did that with tr:nth-child(n+2).
I then need to find the third cell in each of these rows. The descendant or child selectors can be used for this. The full selector is tr:nth-child(n+2) td:nth-child(3). Repeat that for the next two, only increment the values inside td:nth-child.
Since we need to iterate through all these values, I created three separate arrays to store the values inside the third, fourth and fifth columns respectively. It's important to note that these values are of type string and they need to be converted to integers. To do that simply add a + sign before the string. Iterating over the number of elements in each query, I populated the arrays.
Now we need to add up all of these items, and we can do that with the reduce method.
Now add the textnodes to the cells, the cells to the row and finally the row to the table. Finally, set the summedItems variable to true to prevent crazy behavior.
var button = document.getElementById("total-items");
var table = document.getElementById("my-table");
var summedItems = false;
function sumItems() {
if (!summedItems) {
var row = document.createElement("tr");
var branch = document.createElement("td");
var division = document.createElement("td");
var tallyA = document.createElement("td");
var tallyB = document.createElement("td");
var tallyC = document.createElement("td");
var branchText = document.createTextNode("Total");
var sumA = document.querySelectorAll("tr:nth-child(n+2) td:nth-child(3)");
var sumB = document.querySelectorAll("tr:nth-child(n+2) td:nth-child(4)");
var sumC = document.querySelectorAll("tr:nth-child(n+2) td:nth-child(5)");
var aSums = [], bSums = [], cSums = [];
for (var i = 0; i < sumA.length; i++) {
aSums.push(+(sumA[i].innerHTML));
}
for (var i = 0; i < sumB.length; i++) {
bSums.push(+(sumB[i].innerHTML));
}
for (var i = 0; i < sumC.length; i++) {
cSums.push(+(sumC[i].innerHTML));
}
aSums = aSums.reduce((a,b) => a + b)
bSums = bSums.reduce((a,b) => a + b)
cSums = cSums.reduce((a,b) => a + b)
var tallyAText = document.createTextNode(aSums);
var tallyBText = document.createTextNode(bSums);
var tallyCText = document.createTextNode(cSums);
branch.appendChild(branchText);
tallyA.appendChild(tallyAText);
tallyB.appendChild(tallyBText);
tallyC.appendChild(tallyCText);
[branch, division, tallyA, tallyB, tallyC].forEach((e) => row.appendChild(e)
)
table.appendChild(row);
summedItems = true;
}
}
button.addEventListener("click", function() {
sumItems();
});
<table id="my-table" border="1" style="width:100%">
<tr>
<td>Branch</td>
<td>Division</td>
<td>TallyA</td>
<td>TallyB</td>
<td>TallyC</td>
</tr>
<tr>
<td>Alpha</td>
<td>A101</td>
<td>6</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td>Bravo</td>
<td>B102</td>
<td>16</td>
<td>8</td>
<td>10</td>
</tr>
<tr>
<td>Charlie</td>
<td>C103</td>
<td>11</td>
<td>17</td>
<td>21</td>
</tr>
</table>
<button id="total-items">Total Items</button>
with jQuery:
$("table").each(function() {
var $table = $(this);
$row = $("<tr>")
$row.append("<td>Totale</td>")
var sums = [];
$table.find("tr").each(function(){
var index = 0;
$(this).find("td").each(function() {
if (!sums[index]) sums[index] = 0;
sums[index] += parseInt($(this).html());
index++;
})
})
for(var i=1;i<sums.length;i++) {
var el = sums[i];
if (isNaN(el)) el = "";
$row.append("<td>"+el+"</td>")
}
$table.append($row)
})
jsFiddle
Related
I'm trying to use the insertcell method to add a column to my table but either I'm getting the syntax wrong or it isn't working. I wondered if anyone could explain where I am going wrong?
The table body in the html is populated dynamically with some other JavaScript but I don't think this is the problem as I've tested grabbing some content from that table with an alert box and it works (commented out below):
<!DOCTYPE html>
<script type="text/javascript" src="fullstationxyparser.js">
</script>
<html>
<body>
<table border=1>
<thead>
<tr>
<td>Element Name</td>
<td>x</td>
<td>y</td>
<td>testCol</td>
</tr>
</thead>
<tbody id="stationlist">
</tbody>
</table>
</body>
</html>
function addStationNames() {
var myTable = document.getElementById("stationlist");
var stationListRows = myTable.getElementsByTagName('tr');
for (var i = 1; i < stationListRows.length; i++) {
var cell = stationListRows[i].getElementsByTagName('td');
var stationName = cell[0].innerHTML; //get station id from element Name column
var currentRow = stationListRows[i];
var newCol = currentRow.insertcell(-1);
newCol.innerHTML = stationName;
//alert(stationName);
}
}
In Firefox developer tools, I get TypeError: "currentRow.insertcell is not a function". Perhaps I can't use the insertcell method on a row collection?
In general you can call the insertRow() method on a Table DOM element, followed by calls to the insertCell() method as shown below to dynamically add <td> tags to your table with JavaScript.
Be careful to call insertCell() (with capital C) rather than insertcell() as you are currently doing:
const table = document.querySelector('table');
/* Insert new row */
const row = table.insertRow();
/* Insert cells (td) for row */
const td0 = row.insertCell(0);
const td1 = row.insertCell(1);
const td2 = row.insertCell(2);
const td3 = row.insertCell(3);
/* Populate cells with data */
td0.innerText = 'Foo';
td1.innerText = '3';
td2.innerText = '6';
td3.innerText = 'success';
<table border="1">
<thead>
<tr>
<td>Element Name</td>
<td>x</td>
<td>y</td>
<td>testCol</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
Specific to your code, some other changes to consider might be as listed in this code snippet:
function addStationNames() {
/* Condense table row access into single query */
const stationRows = document.querySelectorAll("#stationlist tr");
stationRows.forEach((stationRow, i) => {
/* Skip first row */
if(i === 0) { return; }
/* Get station name from text of first cell */
const stationName = stationRow.querySelector('td:first-child').innerText;
/* Insert last cell on row and assign station name */
stationRow.insertCell(-1).innerText = stationName;
});
/*
Old code:
for (let i = 1; i < stationListRows.length; i++) {
var cell = stationListRows[i].getElementsByTagName('td');
var stationName = cell[0].innerHTML;
var currentRow = stationListRows[i];
var newCol = currentRow.insertcell(-1);
newCol.innerHTML = stationName;
}
*/
}
addStationNames();
<!-- set table id to stationlist -->
<table border="1" id="stationlist">
<thead>
<tr>
<td>Element Name</td>
<td>x</td>
<td>y</td>
<td>testCol</td>
</tr>
<tr>
<td>90's pop</td>
<td>232</td>
<td>543</td>
</tr>
</thead>
<tbody>
<!-- Remove id from tbody -->
</tbody>
</table>
An alternative to the answer above (which is totally fine) is this method, which is also a more general method of creating any html element:
const table = document.getElementById('one');
const newRow = document.createElement("tr");
let newCell = document.createElement("td");
newCell.textContent = "first cell";
let newCell2 = document.createElement("td");
newCell2.textContent = "second cell";
newRow.appendChild(newCell);
newRow.appendChild(newCell2);
table.appendChild(newRow);
https://jsfiddle.net/zgaosdbv/
I use this code to get the index of a td in a table:
var tdIndex = this || (e || event).target;
var index = tdIndex.cellIndex;
The problem is that each row's index starts again at 0;
For example, the index of the first cell of the first row is 0. Similarly, The index of the first cell in the second row is also 0, and so on. I don't want the index to be reset to 0 for each row in a table. I want the index number to continue where it left off in the last cell of the previous row.
Ask your cell for it's parent row then get the rowIndex.
var rowIndex = e.target.parentElement.rowIndex;
The above code assumes that e is your event and target is your cell. Your (e || event) code isn't really necessary since you decide what your event object is named based on how your declare your event handler.
You'll need take into account the rowIndex property to get unique number for the cell in each.
Ideally each cell is defined in JS as a unique pair of (rowIndex,cellIndex)
var tdIndex = this || (e || event).target;
var index = tdIndex.cellIndex + 1000 * tdIndex.rowIndex;
note that you can use any number instead of 1000. It should just be greater than the maximum number of columns in the table
Something like this may do the trick and will work even if the number of cells is not the same on every row:
const table = document.querySelector('#table__base');
const cellOne = document.querySelector('#cellOne');
const cellTwo = document.querySelector('#cellTwo');
function getIndex(table, cell) {
let counter = 0;
for (let i = 0, len = cell.parentNode.rowIndex; i <= len; i += 1) {
let numberOfCellsInThisRow = (i === cell.parentNode.rowIndex) ? cell.cellIndex : table.rows[i].cells.length;
counter += numberOfCellsInThisRow;
}
return counter;
}
console.log(getIndex(table, cellOne));
console.log(getIndex(table, cellTwo));
<table id="table__base">
<tr>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td>6</td>
<td>7</td>
<td>8</td>
<td id="cellOne">9</td>
<td>10</td>
<td>11</td>
</tr>
<tr>
<td>12</td>
<td>13</td>
<td id="cellTwo">14</td>
<td>15</td>
<td>16</td>
<td>17</td>
</tr>
</table>
I'm a beginner with code,
I'm trying to run on this table and get the text from each .winner class and push it to an Array, so instead of getting:
["aa","aa","dd"]
I'm getting
["aaaadd","aaaadd","aaaadd"]
$(document).ready(function(){
var arr = [];
var winner = $('.winner').text() ;
for ( i = 0; i < $('table').length ; i++ ) {
arr.push(winner);
}
console.log(arr);
});
HTML:
<table>
<tr>
<td>#</td>
<td class="winner">aa</td>
<td>bb</td>
<td>cc</td>
<td>dd</td>
</tr>
</table>
<table>
<tr>
<td>#</td>
<td class="winner">aa</td>
<td>bb</td>
<td>cc</td>
<td>dd</td>
</tr>
</table>
<table>
<tr>
<td>#</td>
<td class="winner">dd</td>
<td>cc</td>
<td>bb</td>
<td>aa</td>
</tr>
</table>
I guess something is wrong with my for loop
var arr = [];
$('table .winner').each(function () {
arr.push($(this).text());
})
Example
or version without class .winner
$('table').each(function () {
arr.push($(this).find('tr').eq(0).find('td').eq(1).text());
});
Example
$('table .winner') - returns 3 td's with class .winner
$(this).text() - get text from current element.
In your example $('.winner').text() returns text "aaaadd", then you get $('table').length (will be 3) and three times push the same text to arr
The sentence
var winner = $('.winner')
will give you an array of objects, so you need to loop each of them and call text() method for each one.
With this:
var winner = $('.winner').text();
You are getting a combined texts from all the td elements marked as winner (see docs here).
Then, for each table, to push this value to the array:
for ( i = 0; i < $('table').length ; i++ ) {
arr.push(winner);
}
This is actually not necessary.
What you want is probably:
var winners = $('.winner');
for (var i = 0; i < winners.length(); ++i) {
arr.push(winners.eq(i).text());
}
Hi I have html structure with table. I want to sort td according to their value. I trying it but cant find the logic to make it happen. My function is
<head>
<script type="text/javascript">
function sorting(){
var sortvalue= document.getElementsByTagName('td');
for(i=0; i<sortvalue.length;i++){
var val= sortvalue[i].value
}
}
</script>
</head>
<body>
<table width="500" border="0" cellspacing="0" cellpadding="0">
<tr>
<td>5</td>
</tr>
<tr>
<td>3</td>
</tr>
<tr>
<td>2</td>
</tr>
<tr>
<td>4</td>
</tr>
<tr>
<td>1</td>
</tr>
</table>
click to sort
</body>
If you plan to do more than just organize those numbers: those saying you should use a plugin are correct. It'd take more effort than it's worth to try to make your own table sorter.
If all you want to do is sort those numbers (small to large):
function sorting() {
td = document.getElementsByTagName("td");
sorted = [];
for (x = 0; x < td.length; x++)
sorted[x] = Number(td[x].innerHTML);
sorted.sort();
for (x = 0; x < sorted.length; x++)
td[x].innerHTML = sorted[x];
}
Largest to smallest:
function sorting() {
td = document.getElementsByTagName("td");
sorted = [];
for (x = 0; x < td.length; x++)
sorted[x] = Number(td[x].innerHTML);
sorted.sort().reverse();
for (x = 0; x < sorted.length; x++)
td[x].innerHTML = sorted[x];
}
Assuming that you're putting the script under your link, or adding it on domready:
function sorting(){
var tbl = document.getElementsByTagName('table')[0];
var store = [];
for(var i=0, len=tbl.rows.length;i<len; i++){
var row = tbl.rows[i];
var sortnr = parseFloat(row.cells[0].textContent || row.cells[0].innerText);
if(!isNaN(sortnr)) store.push([sortnr, row]);
}
store.sort(function(x,y){
return x[0] - y[0];
});
for(var i=0, len=store.length; i<len; i++){
tbl.appendChild(store[i][1]);
}
store = null;
}
link here: http://jsfiddle.net/UMjDb/
For your example you can make an array of the cell data from the node list and sort that array, and then replace the cells data with the sorted data. Simpler than moving elements.
<head>
<script type= "text/javascript">
function sorting(){
var T= [], tds= document.getElementsByTagName('td');
for(var i= 0;i<tds.length;i++){
T.push(tds[i].firstChild.data);
}
T.sort(function(a, b){
return a-b
});
for(var i= 0;i<tds.length;i++){
tds[i].replaceChild(document.createTextNode(T[i]), tds[i].firstChild);
}
}
</script>
</head>
<body>
<table width= "500" border= "0" cellspacing= "0" cellpadding= "0">
<tr>
<td>5</td>
</tr>
<tr>
<td>3</td>
</tr>
<tr>
<td>2</td>
</tr>
<tr>
<td>4</td>
</tr>
<tr>
<td>1</td>
</tr>
</table>
click to sort
</body>
i giving a jquery solution, hope this post helps you.
var tdData = Array();
$(document).ready(function(){
$('td').each(function(i){
tdData [i] = $(this).text();
});
});
function sorting(){
var sortedData = tdData.sort();
$('td').each(function(i){
$(this).text(sortedData[i]);
} );
}
complete solution: link
step 1: find all td's
step 2: fetch their values
step 3: sort them on their values
step 4: put them back into their parent in the correct order. This can simply be done with an $(parent).html("").append(sortedNodes) (if you use jQuery that is).
As #FelixKling points out below, the .html("") is not strictly necessary other than for code clarity since "If you have nodes that already exist in the tree, then .append will remove them first from their current location and add them to the new parent"
You need to re-organise the table.
The simplest approach would be to use a plugin like this one for jQuery
You need to modify the DOM, they would be different way to do that, like grabbing all the data, removing all the rows and adding them back in the right order.
It could be improved better using detach and reattaching.
I want to append a new row in my table using Javascript but the new row should be like my previous rows.
I am using CSS to format my rows.
If you are using jQuery it will be something like this:
$(document).ready(function() {
$("table tr:last").clone().appendTo("table");
})
Replacing table with the id or class of your table (unless you only plan to have one table).
Using good old Node.cloneNode(deep) along with HTMLTableElement.rows:
HTML:
<table id="foo">
<tbody id="bar">
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
</tbody>
</table>
JS:
var foo = document.getElementById("foo");
var bar = document.getElementById("bar");
var rows = foo.rows;
var lastRow = rows[rows.length-1];
var newRow = cloneRow(lastRow, true, bar);
function cloneRow(row, append, parent)
{
var newRow = row.cloneNode(true);
if(append)
{
if(parent)
{
parent.appendChild(newRow);
}
}
return newRow;
}
For comparison's sake, here's my code juxtaposed against the jQuery answer: http://jsperf.com/dom-methods-vs-jquery-with-tables
Let's say that your row has a css style of .row
So you could just use DOM methods to create a new tr, add the class="row" then add the appropriate amount of td
Here is a solution that avoids the use of the cloneNode method, and that do not copy the content, data or events of the previous last row, just its CSS attributes and class names.
The new row will have the same number of cell as the previous last row.
var lastRow = myTable.rows[myTable.rows.length-1];
var newRow = myTable.insertRow();
newRow.className = lastRow.className;
newRow.style.cssText = lastRow.style.cssText;
for (var i = 0; i < lastRow.cells.length; i++) {
var newCell = newRow.insertCell(i);
newCell.className = lastRow.cells[i].className;
newCell.style.cssText = lastRow.cells[i].style.cssText;
}