/********************************************************************** Table sorting script By Mark Wilton-Jones 01/7/2005 Version 1.1.0 updated 25/03/2010 to support custom sort functions *********************************************************************** Please see http://www.howtocreate.co.uk/jslibs/ for details and a demo of this script Please see http://www.howtocreate.co.uk/jslibs/termsOfUse.html for terms of use Sorts tables (vertically) based on the data they contain. It is able to sort based on either lexical (text) or numeric content. Sorting can be based on the contents of any chosen column, and can use either ascending or descending order. It can be told to ignore heading rows, and rows with specific classes, and can also be told to sort only a specific table body instead of the whole table. Script runs in DOM browsers. To use: Inbetween the <head> tags, put: <script src="PATH TO SCRIPT/tablesort.js" type="text/javascript"></script> The script provides a single function: sortTableRows(tableRef,column,sortType,order,rowsToIgnore[,optional: classPatternToIgnore]) tableRef = node; reference to either a table or tbody element - if tableRef is a table, the first tbody will be sorted (or the whole table if there is no tbody in the source) column = integer; the column to use to determine sort order (starting from 0) sortType = boolean; true = numeric, false = lexical function; custom function to use for Array.sort like normal array.sort custom functions, it must return <0, 0, or >0 will be passed 4 parameters each time it is called: string; row A cell text content string; row B cell text content object; row A cell DOM node object; row B cell DOM node order = boolean; true = smallest first, false = largest first rowsToIgnore = integer; number of initial rows to ignore (for example, heading rows) classPatternToIgnore = optional regular expression; pattern of tr tag class attributes to ignore - null or undefined if none The script will not sort the table if there are less than 2 rows to be sorted. Any rows that are sorted will be put after any rows that are ignored. For example: if( document.getElementById ) { var foo = document.getElementById('mytable'); sortTableRows(foo,3,true,false,2,/\bheadrow\b|\boddrow\b/); } The script does not take rowspan or colspan into account when selecting columns, and will fail with errors if invalid parameters are used. It should be able to cope with (X)HTML within the table cells (sorting will be based on a concatenation of all text nodes). ________________________________________________________________________________*/ function sortTableRows(oTableItem,oColumn,oSortType,oOrder,oIgnoreRows,oIgnoreClass) { if( !document.childNodes || !document.appendChild ) { return; } //prepare the custom sorting function var trimSpace = /^\s+|\s+$/g, oPadding = unescape('%00'), customFunction = ( typeof(oSortType) == 'function' ); var sortFunc = function (a,b) { var c = totalValueOf(a.quickCells[oColumn]).replace(trimSpace,''); var d = totalValueOf(b.quickCells[oColumn]).replace(trimSpace,''); if( customFunction ) { //custom sort function return oSortType( c, d, a.quickCells[oColumn], b.quickCells[oColumn] ) * ( oOrder ? -1 : 1 ); } if( oSortType ) { //numeric sort c = parseFloat(c); d = parseFloat(d); if( isNaN(c) ) { c = oOrder ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; } if( isNaN(d) ) { d = oOrder ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; } //'Infinity - Infinity' is NaN, and IE throws an error return ( c == d ) ? 0 : ( oOrder ? ( c - d ) : ( d - c ) ); } //alpha sort (default) while( c.length > d.length ) { d += oPadding; } while( d.length > c.length ) { c += oPadding; } return ( c == d ) ? 0 : ( ( oOrder ? ( d > c ) : ( c > d ) ) ? -1 : 1 ); }; //prepare the function used to get the text content of the cells - rudimentary, but should hardly be needed var totalValueOf = function (oNode) { var oStr = ''; for( var i = 0, j; i < oNode.childNodes.length; i++ ) { j = oNode.childNodes[i]; if( j.nodeType == 3 ) { oStr += j.nodeValue; } else if( j.nodeType == 1 ) { oStr += totalValueOf( j ); } } return oStr; }; if( oTableItem.tagName.toLowerCase() == 'table' ) { oTableItem = oTableItem.getElementsByTagName('tbody')[0]; //failed to find a table body - do not sort if( !oTableItem ) { return; } } var usableRows = [], actualRows = 0; for( var i = 0, j; i < oTableItem.childNodes.length; i++ ) { j = oTableItem.childNodes[i]; if( j.tagName && j.tagName.toLowerCase() == 'tr' ) { actualRows++; if( ( actualRows > oIgnoreRows ) && ( !oIgnoreClass || !j.className || !j.className.match(oIgnoreClass) ) ) { //we want to sort this row - get accurate refs to the cells (th/td) j.quickCells = []; for( var n = 0, m; n < j.childNodes.length; n++ ) { m = j.childNodes[n]; if( m.tagName && ( m.tagName.toLowerCase() == 'td' || m.tagName.toLowerCase() == 'th' ) ) { j.quickCells[j.quickCells.length] = m; } } usableRows[usableRows.length] = j; } } } if( usableRows.length > 1 ) { //enough rows to sort usableRows.sort(sortFunc); if( window.ActiveXObject && navigator.platform.indexOf('Mac') + 1 && !navigator.__ice_version && ( !window.ScriptEngine || ScriptEngine().indexOf('InScript') == -1 ) && !window.opera ) { //IE Mac will crash - avoid the bug - slower so I will not use this "fix" in browsers that do not need it for( var i = 0; i < usableRows.length; i++ ) { if( usableRows[i] != oTableItem.lastChild ) { oTableItem.insertBefore(usableRows[i],oTableItem.lastChild); oTableItem.insertBefore(oTableItem.lastChild,oTableItem.lastChild.previousSibling); } } } else { for( var i = 0; i < usableRows.length; i++ ) { oTableItem.appendChild(usableRows[i]); } } //fix Opera rendering bug document.body.className = document.body.className; } }