How to sort a table knowing row position? - javascript

I have a table:
<table>
<tr><td>1</td></tr>
<tr><td>2</td></tr>
<tr><td>3</td></tr>
</table>
An array that tells where every row should come [{index: 2},{index: 1},{index: 0}] (first row is the last from the array, second row is the 1 in array and third row 0 from the array).

Here is my approach.
// create a new temporary tbody outside the DOM (similar to jQuery's detach)
var tbody_tmp = document.createElement('tbody');
// itterate through the array in the order of a new table
for(var i = 0, j = data.length; i < j; i++)
{
// make a copy of current row (otherwise, append child removes the row from the rows array and messes up the index-finder; there got be a better way for this)
var row = rows[data[i].index].cloneNode(true);
tbody_tmp.appendChild(row);
// reset the index to reflect the new table order (optional, outside the sample)
data[i].index = i;
}
// Note that tbody is a jquery object
tbody.parent()[0].replaceChild(tbody_tmp, tbody[0]);
Though, the cloning approach is slow. With 10,000+ records it takes ~1200ms. Furthermore, a jQuery-less approach is preferable.
Posting this in case someone else might find it simple enough for their needs (with less than 1,000 rows).
After hours of restless thinking, I've ended up with the following. If this sample isn't enough, I've written a whole blog post explaining the logic behind it, http://anuary.com/57/sorting-large-tables-with-javascript.
// Will use this to re-attach the tbody object.
var table = tbody.parent();
// Detach the tbody to prevent unnecessary overhead related
// to the browser environment.
var tbody = tbody.detach();
// Convert NodeList into an array.
rows = Array.prototype.slice.call(rows, 0);
var last_row = rows[data[data.length-1].index];
// Knowing the last element in the table, move all the elements behind it
// in the order they appear in the data map
for(var i = 0, j = data.length-1; i < j; i++)
{
tbody[0].insertBefore(rows[data[i].index], last_row);
// Restore the index.
data[i].index = i;
}
// Restore the index.
data[data.length-1].index = data.length-1;
table.append(tbody);

Related

Javascript calculation is not working as expected

I have a gridview in which there is a row in which I add integer values like 7000.
So if there are 2-3 rows added, I want to add all those rows values and show it in the textbox
So for that, I have written the below code
if (document.getElementById('txtTotalExp').value == "") {
var Expense = 0;
} else {
var Expense = document.getElementById('txtTotalExp').value;
}
for (var i = 0; i < GridExpInfo.Rows.length; i++) {
var totAmount = GridExpInfo.Rows[i].Cells[7].Value;
var totval = totAmount;
Expense += parseInt(totval);
}
document.getElementById('txtTotalLandVal').value = parseInt(TotalPayableValue) + parseInt(Expense);
But In the textbox it is coming something like this
200010001512
The addition operation is not working.
I interpret your question to mean that you have a table with rows of values, you want to sum each row, and then you want to get a total sum-of-the-row-sums for the entire table.
I wanted to provide a working code snippet to demonstrate a solution to your problem. However, because you didn't provide all the necessary code, I invented some of it. In the process of doing so, I provided an alternative, and perhaps more modern, approach to solving the overall problem I think you were trying to solve. Yes, it's providing significantly more than what you were asking for (...I think some of the other answers that discuss parseInt might solve your initial problem) but hopefully this gives you some further insight.
The example below shows how to do this using many recent features of JavaScript. Hopefully the comments explain what is happening at every step in enough detail that you get a sense of the logic. To understand each step, you are probably going to have dig deeper into learning more JavaScript. A good reference is the Mozilla Developer Network (MDN), which contains documentation on all these features.
// when the button is clicked, do the following...
document.querySelector('button').onclick = () => {
// calculate the total sum across the table
const rowSums =
// get a nodeList of all table rows in the entire document
// and convert that array-like nodeList to a true array
// of table row elements
[...document.querySelectorAll('tr')]
// remove the first row, i.e. ignore the row of column headers
.slice(1)
// from the original array of table rows (minus the first row)
// create a new array in which each element is derived
// from the corresponding table row from the original array
.map(row => {
// calculate the sum of values across this row
const rowSum =
// get a nodeList of all table cells in this row
// and convert that array-like nodeList to a true array
// of table cell elements
[...row.querySelectorAll('td')]
// remove the last cell, i.e. ignore the cell that will eventually
// hold the sum for this row
.slice(0,-1)
// from the array of table cells for this row (minus the last one)
// derive a new single value by progressively doing something
// for each cell
.reduce(
// for each table cell in this row, remember the cell itself
// as well as the accumulating value we are gradually deriving from all
// the cells in this row, i.e. the sum of all values across this row
(accumulatingRowSum, cell) =>
// for each cell in this row, add the numerical value
// of the current cell to the sum of values we are accumulating
// across this row
accumulatingRowSum + parseInt(cell.innerHTML, 10),
// start our accumulating sum of values across this row with zero
0
);
// get all the cells in this row
const rowCells = row.querySelectorAll('td');
// put the sum of values from this row into the last cell of the row
rowCells[rowCells.length - 1].innerHTML = rowSum;
// place the sum of values in this row into the accumulating array
// of all such values for all rows
return rowSum;
});
// calculate the total sum for the whole table
const totalExpenses =
// start with the array of sums for each row
rowSums
// similar to `reduce` above, reduce the array of multiple row sums
// into a single value of the total sum, calculated by adding together
// all the individual row sums, starting with zero
.reduce((accumulatingTotalSum, rowTotal) => accumulatingTotalSum + rowTotal, 0);
// place the total sum into the appropriate span element
document.querySelector('#txtTotalExp').innerHTML = totalExpenses;
};
table {
border: solid black 1px;
border-collapse: collapse;
}
th, td {
border: solid black 1px;
padding: 0.5em;
}
<table>
<tr><th>col1</th><th>col2</th><th>row total</th></tr>
<tr><td>3500</td><td>1200</td><td></td></tr>
<tr><td>2700</td><td>4500</td><td></td></tr>
<tr><td>3100</td><td>1300</td><td></td></tr>
</table>
<p>The total expenses are: <span id="txtTotalExp"></span></p>
<button>Calculate</button>
you can used below code:
var total=parseInt(TotalPayableValue,10) + parseInt(Expense,10);
document.getElementById('txtTotalLandVal').value=total;
try this:
var Expense = 0;
if (document.getElementById('txtTotalExp').value != "") {
var Expense = parseInt(document.getElementById('txtTotalExp').value);
}
for (var i = 0; i < GridExpInfo.Rows.length; i++) {
var totAmount = GridExpInfo.Rows[i].Cells[7].Value;
Expense += parseInt(totAmount);
}
The problem: Expense initially can be a string. Have you tried if with "Expense = 0" the error occurs?

Remove all child nodes but the first one

I have an HTML table. The first row contains a checkbox. There is a javascript method associated to the checkbox change. If the checkbox is checked, the code adds a few rows to the table and fills them. If the checkbox is unchecked, the code removes all rows but the first one (the one that contains the checkbox).
The first part of the code works fine : the rows are properly added.
I have an issue with the second part. Here is my code :
if (checkboxValue) {
//Add first row
var tr1 = document.createElement("tr");
var td1_1 = document.createElement("td");
....
tr1.appendChild(td1_1);
var td1_2 = document.createElement("td");
...
tr1.appendChild(td1_2);
table.appendChild(tr1);
//Add second row
var tr2 = document.createElement("tr");
var td2_1 = document.createElement("td");
...
tr2.appendChild(td2_1);
var td2_2 = document.createElement("td");
...
tr2.appendChild(td2_2);
table.appendChild(tr2);
} else {
//Remove all rows but the first
var rows = table.getElementsByTagName("tr");
var nbrRows = rows.length;
if (nbrRows > 1) {
for (var i = 1; i < nbrRows; i++) {
var row = rows[i];
row.parentNode.removeChild(row);
}
}
}
The issue always come from rows[2] being undefined. I have no idea why!
If, instead of using removeChild, I write row.innerHTML = "", I have the visual effect I am looking for (all rows gone), but this is inelegant, since the table now contains several empty rows (their number increasing everytime I check/uncheck the checkbox).
A clue? Thank you very much for your time!
Don't use for-loop to remove DOM elements like this. The problem is that rows is a live collection, meaning that it updates every time you remove elements from DOM. As the result, i counter shifts and eventually you hit "undefined" element spot.
Instead, use while loop. For example, to remove all rows except the first one you could do:
var rows = table.getElementsByTagName("tr");
while (rows.length > 1) {
rows[1].parentNode.removeChild(rows[1]);
}
Also note, that it's getElementsByTagName without s.
UPD. Or iterate backward if you like for-loops better:
var rows = table.getElementsByTagName("tr");
for (var i = rows.length - 1; i > 0; i--) {
rows[i].parentNode.removeChild(rows[i]);
}
Demo: https://jsfiddle.net/9y03co6w/
you remove a row from the array you are iterating over. This is always a bad idea and probably the reason for your error.
solution: start iterating from the end instead of the beginning.
also see for example: example
try to replace this line
var rows = table.getElementsByTagNames("tr");
by
var rows = table.find("tr");

Set HTML table rows all at once

I currently have code that runs through every row of a html table and updates it with a different row.
Here is the code
function sort(index) {
var rows = $table.find('tbody tr');
var a = $table.find('tbody tr');
//Only sort if it has not been sorted yet and the index not the same
if(sortedIndex === index){
for(var i = 0; i < rows.length; i++){
a[i].outerHTML = rows[(rows.length - i)-1].outerHTML;
}
toggleSorted();
}else{
sortedIndex = index;
rows.sort(naturalSort);
for (var i = 0; i < rows.length; i++) {
a[i].outerHTML = rows[i].outerHTML;
}
sortedDown = true;
}
$('#${tableId}').trigger('repaginate');
};
What I am trying to do is to instead of going through every single row in the for loop and setting a[i].outterHTML = rows[i].outterHTML; I would like to just set all of the rows at once. Currently it takes about 1.5 seconds to set them and that is very slow.... Only issue is I cannot seem to find a way to do this. Is this actually possible? (It takes 1.5 seconds on large data sets which is what I am working with).
Since the rows are the same just reordered, you can .append them with the new order:
var $tBody = $table.find('tbody');
var $rows = $tBody.find('tr');
if(sortedIndex === index){
toggleSorted();
$tBody.append($rows.get().reverse());
}
else {
sortedIndex = index;
sortedDown = true;
$tBody.append($rows.get().sort(naturalSort));
}
Here's a fiddle that demonstrates the above: http://jsfiddle.net/k4u45Lnn/1/
Unfortunately the only way to "set all of your rows at once" is to loop through all of your rows and perform an operation on each row. There may be some libraries which have methods and functions that make it look like you're doing the operation on all of your rows at one shot, but ultimately if you want to edit every element in a set you need to iterate through the set and carry out the action on each element, seeing as HTML doesn't really provide any way to logically "link" the attributes of your elements.

Generate table based on number of rows, columns in jquery

How do I generate a table in jQuery based on a given number of rows and columns?
You can use nested for loops, create elements and append them to one another. Here's a very simple example that demonstrates creating DOM elements, and appending them.
You'll notice that the $('<tag>') syntax will create an element, and you can use the appendTo method to, well, do exactly that. Then, you can add the resulting table to the DOM.
var rows = 5; //here's your number of rows and columns
var cols = 5;
var table = $('<table><tbody>');
for(var r = 0; r < rows; r++)
{
var tr = $('<tr>');
for (var c = 0; c < cols; c++)
$('<td>some value</td>').appendTo(tr); //fill in your cells with something meaningful here
tr.appendTo(table);
}
table.appendTo('body'); //Add your resulting table to the DOM, I'm using the body tag for example

Javascript table creation

Trying to create a table using the following code but not working. Please point out where I'm going wrong.
var i,j;
function cell(ih){
var tcell =document.createElement('td');
tcell.innerHTML=ih;
return tcell;
}
mutable=document.createElement('table');
for (i=0;i<10;i++){
row1=document.createElement('tr');
for(j=0;j<10;j++){
row1.appendChild(cell(j));
}
mutable.appendChild(row1);
document.write(mutable);
}
You have several problems, the first two are the big ones, the second two are a matter of style and risk of clashes with other code:
You are trying to document.write HTMLElementNodes. document.write only deals with strings. Grab a container element (e.g. with document.getElementById) and append to it
You are trying to document.write the entire table every time you add a row to it. Append the table once the table is complete, not every time you go through the loop.
You are using globals all over the place, learn to love the var keyword
row1 is a poor variable name for the row you are operating on which usually isn't the first
Use document.body.appendChild(...) instead of document.write(...).
You can do it by changing your script to use document.body.appendChild(mutable) after your nested for loop:
var i,j;
function cell(ih){
var tcell =document.createElement('td');
tcell.innerHTML=ih;
return tcell;
}
mutable=document.createElement('table');
for (i=0;i<10;i++){
row1=document.createElement('tr');
for(j=0;j<10;j++){
row1.appendChild(cell(j));
}
mutable.appendChild(row1);
}
document.body.appendChild(mutable);
This appends the entire mutable table object you've created to the <body> element of your page. You can see it working here.
Also note that most times in markup, you don't see the <tbody> element, but it is good practice to append this as a child element of the <table> and as a parent element for all of your rows. So, your script should look more like this:
function cell(ih){
var tcell = document.createElement('td');
tcell.innerHTML = ih; // I would suggest you use document.createTextNode(ih) instead
return tcell;
}
function appendTable() { // you now have to call this function some time
mutable = document.createElement("table");
var tBody = mutable.appendChild( document.createElement("tbody") ); // technique using "fluid interfaces"
for (var i = 0; i < 10; i++) {
var row1 = tBody.appendChild( document.createElement('tr') ); // fluid interface call again
for(var j = 0; j < 10; j++) {
row1.appendChild(cell(j));
}
}
document.body.appendChild(mutable);
}
I made some style changes to your script, and I would suggest making even more, but as far as correctness, it should work.

Categories

Resources