Reputation: 599
I have the following code to sort a table using Javascript. What I want to do is make the last row of the table for totals and therefore need to append my code to remove the last row from sorting. I am having troubles with this task.
var TableSort = function ( tableId ) {
var that = this;
// Store a reference to the table node as a property
this.table = document.getElementById(tableId);
// Validate the table node
if ( this.table && this.table.nodeType != 1 ) {
throw new Error("TableSort: Table id is not a DOM element.");
}
if ( this.table.tagName != "TABLE" ) {
throw new Error("TableSort: Table id is not a table element.");
}
// Set three global properties
this.sortColumn = "";
this.sortUp = true;
this.imageNodes = [];
// Get the header nodes
var headers = this.table.getElementsByTagName("th");
// Loop through the header nodes
var header, columnName, imageNode, imageClick;
for ( var i = 0; i < headers.length; i++) {
header = headers[i];
// Create a node for the image
imageNode = document.createElement("img");
// Add the "sort arrow" images to the header column
// And set the first column as the default sort column
columnName = header.className;
if ( i == 0 ) {
this.sortColumn = columnName;
imageNode.src = "arrows_up.png";
} else {
imageNode.src = "arrows_off.png";
}
// Get the event handler for the image
imageClick = this.imageClickHandler(columnName);
// Add the event handler to the image
jsLib.event.add(imageNode, "click", imageClick);
// Add the image node to column header and to array of image nodes
header.appendChild(imageNode);
this.imageNodes[columnName] = imageNode;
}
// Sort the table
this.sort();
}
TableSort.prototype.imageClickHandler = function (columnName) {
var that = this;
// return an event handler
return function () {
// If current sort column, toggle the sort direction
if ( that.sortColumn == columnName ) {
that.sortUp = ! that.sortUp;
// Otherwise...
} else {
// Set the image for the old sort column
that.imageNodes[that.sortColumn].src = "arrows_off.png";
// Switch the current sort column and sort ascending
that.sortColumn = columnName;
that.sortUp = true;
}
// Set the appropriate arrows for the current sort column
if ( that.sortUp ) {
that.imageNodes[that.sortColumn].src = "arrows_up.png";
} else {
that.imageNodes[that.sortColumn].src = "arrows_down.png";
}
that.sort();
}
}
TableSort.prototype.sort = function () {
var that = this;
if ( this.sortColumn == "" ) return;
// Get the rows from the table
var rowNodes = this.table.getElementsByTagName("tr");
if (rowNodes.length <= 1 ) return;
// Store a reference to each row in the rows array
var rows = [];
for ( var i = 0; i < rowNodes.length; i++) {
rows.push( rowNodes[i] );
}
// Get a reference to the tbody node
var tbodyNode = rows[0].parentNode;
// Remove the header row from the array (but not the DOM)
rows.shift();
// Remove all rows except the header row from the DOM
var row;
for ( i = 0; i < (rows.length); i++ ) {
row = rows[i];
row.parentNode.removeChild(row);
}
// Define two functions that check for data types
var isMoney = function ( value ) {
var m = value.replace( /[$,]/g, "" );
return ! isNaN(m);
}
var isDate = function ( value ) {
var d = new Date(value);
return ! isNaN(d);
}
// Define the direction variable
var direction = ( that.sortUp ) ? 1 : -1;
// Define the function that compares the rows
var compareColumnValues = function (rowA, rowB) {
var valueA, valueB;
var i, cell;
// Get the cell value for row A
var cellsA = rowA.getElementsByTagName("td");
for ( i = 0; i < cellsA.length; i++ ) {
cell = cellsA[i];
if ( cell.className == that.sortColumn ) {
valueA = cell.firstChild.nodeValue;
break;
}
}
// Get the cell value for row B
var cellsB = rowB.getElementsByTagName("td");
for ( i = 0; i < cellsB.length; i++ ) {
cell = cellsB[i];
if ( cell.className == that.sortColumn ) {
valueB = cell.firstChild.nodeValue;
break;
}
}
// Convert the values to the appropriate data type
if ( ! isNaN(valueA) && ! isNaN(valueB) ) {
valueA = parseFloat(valueA);
valueB = parseFloat(valueB);
} else if ( isDate(valueA) && isDate(valueB) ) {
valueA = new Date(valueA);
valueB = new Date(valueB);
} else if ( isMoney(valueA) && isMoney(valueB) ) {
valueA = parseFloat(valueA.replace( /[$,]/g, "" ));
valueB = parseFloat(valueB.replace( /[$,]/g, "" ));
}
// Compare the two values and return the appropriate comparison value
if ( valueA < valueB ) {
return -1 * direction;
} else if ( valueB < valueA ) {
return 1 * direction;
} else {
return 0;
}
}
// Use the compareColumnValues function to sort the rows array
rows.sort( compareColumnValues );
// Add all rows back to the DOM
for ( i = 0; i < rows.length; i++) {
tbodyNode.appendChild( rows[i] );
}
}
Upvotes: 0
Views: 2306
Reputation: 52
I found a great solution, which I actually used in code for my job! I can't take the credit for it however, but I just wanted to point y'all in the right direction:
http://www.kryogenix.org/code/browser/sorttable/
"1. Download the Javascript library here: http://www.kryogenix.org/code/browser/sorttable/sorttable.js
2.Include the Javascript library, by putting a link to it in the HEAD of your page, like so:
3.Mark your table as a sortable one by giving it a class of "sortable":
Note that the library's JavaScript file is called sorttable (two Ts), but the class you add to the table is sortable (one T)."
The setup is pretty straightforward and should allow for both ascending and descending sorts. Let me know if you have any questions.
Thanks! Paul
Upvotes: 2
Reputation: 1842
You could use a bit more structure in your HTML markup to help your code figure out what can be sorted.
Put the totals row in a tfoot
element and the sortable rows in a tbody
element ... now you can target the sortable rows e.g.
var sortableRows = this.table.getElementsByTagName('tbody'),
rowNodes = sortableRows.getElementsByTagName('tr');
Update: Since you can't change the markup, remove the last row from your sorting array and cache it.
...
// Remove the header row from the array (but not the DOM)
rows.shift();
// And now we remove the footer row and cache it
var footerRow = rows.pop();
and then modify your code to write the sorted rows back to the DOM using insertBefore
...
// Add all rows back to the DOM, before the footer row
for ( i = 0; i < rows.length; i++) {
tbodyNode.insertBefore(rows[i], footerRow);
}
That loop is actually a bit of a performance problem, since you're making multiple writes to the DOM. It would be better to write all those sorted nodes to a documentFragment
and just write to the DOM once, as follows:
...
var sortedFragment = document.createDocumentFragment();
// Write all the sorted rows to a fragment
for ( i = 0; i < rows.length; i++) {
sortedFragment.appendChild(rows[i]);
}
// Insert fragment containing sorted rows before footer
tbodyNode.insertBefore(sortedFragment, footerRow);
Upvotes: 4