browserhome :: write me :: leo :: mozer ::
A javascript sorter for html tables using the document object model (DOM).
Just click on the column heads to sort the table. Try subsequent sorts of different columns.
| Kd-Nr | Name | Vorname | controlNo |
|---|---|---|---|
| 9 | Laurel | Stan | 0 |
| 9 | Laurel | Oliver | 1 |
| 9 | Hardy | Stan | 2 |
| 9 | Hardy | Oliver | 3 |
| 10 | Laurel | Stan | 4 |
| 10 | Laurel | Oliver | 5 |
| 10 | Hardy | Stan | 6 |
| 10 | Hardy | Oliver | 7 |
Look at the source code of this page as an example.
Important: The table needs a thead and tbody section!
/** Javascript table sorter and debug utilities
CVS: $Id: tableSorter.js,v 1.12 2006/02/09 09:50:28 becki Exp $
Author: Stefan Beckert ( http://becki.skiclub-mitwitz.de )
License: Public Domain
Encoding: UTF-8
Sorting is tested and works with Mozilla Firefox 1.5, Konqueror 3.2.3
and IE6.
The work is done on the client side, no AJAX is necessary.
*/
/** Set to mozilladump & start Gecko based Browsers with -console parameter for
debug output (Doesn't work with Internet Explorer)
Set to newwindow for any browser
*/
//var debug = new Logger('TabSort', 'suppress');
//var debug = new Logger('TabSort', 'mozilla');
//var debug = new Logger('TabSort', 'extwindow');
/** Reference to the TableSorter object as global variable.
Necessary because within the comparator function is no "this" available
*/
var tsObj;
/** Callback for array sorting algorithm.
Global Function, does not belong to class TableSorter.
The sorting algorithm of javascript isn't stable, ie.
it doesn't respect the order of equal elements coming from previous sorts.
This funcion takes this (bad) behaviour into account and emulates a stable
sort by holding a sort history and doing subsorts of equal elements.
For 'stable' explanation see:
http://java.sun.com/docs/books/tutorial/collections/algorithms/
*/
function compare(a, b) {
for (var ix=0; ix<tsObj.sortCols.length; ix++) {
var col= tsObj.sortCols[ix];
// We need new local varibles, because overwriting of function args
// a & b doesn't work with IE 6.0:
var c= a.txts[col];
var d= b.txts[col];
if (c > d) return tsObj.sdir[col];
if (c < d) return -tsObj.sdir[col];
}
return 0;
}
/** Maintains sort history.
Public method of class TableSorter, can be used to modify sort history.
Sort history works like a stack which can't contain duplicate elements.
The index of newest sorted col is insterted at the beginning
*/
function addToSortHist(col) {
if (this.sortCols.length==0 || this.sortCols[0]!=col) {
// user clicked on a new column:
for (var i=0; i<this.sortCols.length; i++) {
// remove this col in the sort history:
if (this.sortCols[i] == col) this.sortCols.splice(i,1);
}
this.sortCols.unshift(col); // insert new col as first element
}
}
/** Changes mouse cursor to wait symbol and status bar to 'sorting...'.
Public method of class TableSorter
Can be called with onmousedown before doSort() is called with onclick
*/
function initSort(obj) {
obj.style.cursor= 'wait';
window.status= 'sorting...';
}
/** Sort the table after column number 'scol'.
Public and most important method of class TableSorter
To be called with with 'onclick'
*/
function doSort(scol) {
//debug.lf();
tsObj= this; // compare() method needs a ref to TableSorter Object
// get text content of col when sorted first time after this col:
if (this.trNodes[0].txts[scol] == null) {
var numeric= true;
for (var line=0; line<this.trCnt; line++) {
var bf = this.trNodes[line].node.getElementsByTagName("td");
bf= bf[scol];
while ((bf.nodeType != 3) && bf.firstChild) bf= bf.firstChild;
bf= bf.nodeValue;
if (!bf) bf=''; // for empty table cells
if (isNaN(bf)) numeric= false;
this.trNodes[line].txts[scol] = bf;
}
for (var line=0; line<this.trCnt; line++) {
var bf= this.trNodes[line].txts[scol];
if (numeric) bf= parseFloat(bf);
else if (this.ignoreCase) bf= bf.toLowerCase();
this.trNodes[line].txts[scol]= bf;
}
//debug.log('col '+ scol+ ' numeric: '+ numeric);
}
// set sort direction and maintain sort history:
this.sdir[scol]= -this.sdir[scol]; // toggle sort direction
this.addToSortHist(scol);
// purge the table:
for (var line=0; line<this.trCnt; line++)
this.tbody.removeChild(this.trNodes[line].node);
// do the sorting:
//var start = new Date().getTime(); // measure sorting time
this.trNodes.sort(compare); // sort
//debug.log('col '+ scol+ ': '+ ((new Date().getTime()) - start)+ 'ms' );
//debug.log('hist: '+ this.sortCols);
// refill the table:
for (var line=0; line<this.trCnt; line++)
this.tbody.appendChild(this.trNodes[line].node);
// set the sorting indicator at column header.
if (this.sortCols.length > 1) { // remove old indicator if present
var bf= this.sortCols[1]; // get the old column number
bf= this.thIdx[bf]; // get the corresponding th col number
this.thAdrs[bf].firstChild.nodeValue= this.thText[bf];
}
var bf= this.sortCols[0]; // get the new column number
var ind= (this.sdir[bf] > 0) ? '↓' : '↑';
bf= this.thIdx[bf]; // get the corresponding th col number
this.thAdrs[bf].firstChild.nodeValue= ind+ this.thText[bf]+ ind;
// restore cursor and status bar:
this.thAdrs[bf].style.cursor= 'pointer';
window.status= '';
}
/** Constructor of class TableSorter. */
function TableSorter(
tableId, // <table id="tableId">
sortCaseSensitive // defaults to false if omitted i.e. 'ignore case'
) {
this.ignoreCase = !sortCaseSensitive;
// function "pointers" to class methods:
this.doSort = doSort;
this.initSort = initSort; // class method
this.addToSortHist = addToSortHist; // class method
// instance arrays:
this.thIdx = new Array(); // td-index to th-index mapping (th-colspan)
this.thAdrs = new Array(); // refs to the table header nodes
this.thText = new Array(); // table header strings
this.sortCols = new Array(); // sort column history queue
// get table headers:
var buf= document.getElementById(tableId).getElementsByTagName("thead");
buf= buf[0].getElementsByTagName("tr");
var ths= buf[0].getElementsByTagName("th"); // all thead th elements
// get real number of colums (take colspans of th's into account)
for (var col=0; col<ths.length; col++) {
if (! (buf= ths[col].getAttribute('colspan'))) buf= 1;
for (var i=0; i<buf; i++) this.thIdx.push(col);
buf= ths[col];
this.thAdrs[col]= buf;
this.thText[col]= buf.firstChild.nodeValue;
if (buf.getAttribute('onclick')) buf.style.cursor= 'pointer';
/* Firefox supports setting event handling function like this:
buf.setAttribute('onmousedown', tableId+'.initSort(this)');
buf.setAttribute('onclick', tableId+'.doSort('+col+')');
But IE6 doesn't :-( ,therefore setting the sorting functions as
event handler has to be done manually in html table
http://kryogenix.org/code/browser/sorttable/ does it with innerHTML
maybe this is a solution?
*/
}
//debug.log('thIdx: '+ this.thIdx);
// store the real number of columns:
this.colCnt = this.thIdx.length; // number of columns of the table
//debug.log('colCnt: '+ this.colCnt);
// setup sort direction (asc/desc) history for each col:
this.sdir = new Array(this.colCnt); // can only have values of -1 or +1
for (var col=0; col<this.colCnt; col++) this.sdir[col] = -1;
// get table content:
buf= document.getElementById(tableId).getElementsByTagName("tbody");
this.tbody= buf[0]; // later necessary for doSort()
buf= buf[0].getElementsByTagName("tr"); // all tbody tr elements as object
this.trCnt= buf.length; // number of table rows
this.trNodes = new Array(buf.length); // all tbody tr elements as array
for (var line=0; line<this.trCnt; line++)
this.trNodes[line]= new TableRow(buf[line], this.colCnt);
}
/** Abstract data type (struct)
Contains a reference to a table row node and the text content of all
cells of the table row
used by class TableSorter
*/
function TableRow(trNode, colCnt) {
this.node = trNode;
this.txts = new Array(colCnt);
for (var col=0; col<colCnt; col++) this.txts[col] = null;
}