This is version . It is not the current version, and thus it cannot be edited.
[Back to current version]   [Restore this version]

Sortable wiki tables#

Following client-side javascript makes your wiki-tables sortable. (including the tables generated by BrushedTablePlugin) Just click one of the header cells and your table is sorted without round-trip to your server.

vanilla-sort.jpg

It is part of the BrushedTemplate but you can use it standalone as well. -- DF Oct-2005

Usage#

Enclose your tables in %%sortable tags, and klaar-is-kees. Make sure your table starts with a row of all header cells (wikisyntax: || ), otherwise nothing will happen. When all is fine and you move the mouse over one of the clickable header cells, you should now see a tool-tip and a changed mouse pointer.

When clicking a column header, the column will be sorted ascending. Clicking again will reverse the sort order. The sort algorithm will guess the data type of your column (date, numeric or string) and use the appropriate sorting functions.

%%sortable
|| Title || Author || Published   || Edition
| book1  |  zappy  |  25-Feb-2005 |  5
| book2  |  happy  |  25-Jan-2005 |  19
| book3  |  pappy  |  23-Mar-2005 |  06
| book4  |  dappy  |  21-Apr-2005 |  199
| book5  |  rappy  |  25-Jul-2005 |  017
%%

Reality check

Title Author Published Edition
book1 zappy 25-Feb-2005 5
book2 happy 25-Jan-2005 19
book3 pappy 23-Mar-2005 06
book4 dappy 21-Apr-2005 199
book5 rappy 25-Jul-2005 017

Implementation#

The implementation was inspired by the excellent javascript created by Erik Arvidsson. See http://webfx.eae.net/dhtml/sortabletable/sortabletable.html.

However, I refactored it completely so it better fits with JSPWiki pages. JSPWiki tables dont use thead or tbody tags; it needed to be more flexible to change the appearance of the sortAscending/sortDescending controls through css; allowing fine control in different skins; and I wanted automatic recognistion of the data-type of the column to be sorted.

Javascript#

The needed javascript is very powerful, and still pretty small. Everything is included in the Sortable object to keep the .js namespace unpolluted.

The onPageLoad does the initialisation after the page is loaded. (make sure to add a call to this function in your window.onload() handler somewhere) It will track all %%collapse elements and process its first TABLE element. An onclick() handler is added to each column header which points to the heart of the javascript: the Sortable.sort() function.

The sort function has four major steps :

  1. Validate the header row and checking which column was clicked
  2. Copy the table body rows into a javascript array and at the same time find out the data-type of the column to be sorted being date, number or (default) string format
  3. Do the actual sort or reverse sort
  4. Put the sorted array back into the DOM tree of the document

The Sortable.convert() and Sortable.createCompare() are helper functions for data-type conversion and for creation of appropriate comparsion routines used by the javascript sort engine.

All this is included in the BrushedTemplate brushed.js.

/**
 ** 230 Sortable -- for all tables
 **/
var Sortable = new Object();
Sortable.ClassName = "sortable";
Sortable.ClassSort           = "sort";
Sortable.ClassSortAscending  = "sortAscending";
Sortable.ClassSortDescending = "sortDescending";
Sortable.TitleSort           = "Click to sort";
Sortable.TitleSortAscending  = "Ascending order - Click to sort in descending order";
Sortable.TitleSortDescending = "Descending order - Click to sort in ascending order";


Sortable.onPageLoad = function()
{
  var p = document.getElementById( "pagecontent" );
  var sortables = getElementsByClassName( p, Sortable.ClassName );  
  for( i=0; i<sortables.length; i++ )
  {
    var table = sortables[i].getElementsByTagName( "table" )[0];
    if( !table ) continue;
    if( table.rows.length < 2 ) continue;
  
    for( var i=0; i < table.rows[0].cells.length; i++ )
    {
      var c = table.rows[0].cells[i];
      if( c.nodeName != "TH" ) break;
      c.onclick    = function() { Sortable.sort(this); } ;
      c.title      = this.TitleSort;
      c.className += " " + this.ClassSort;
    }        
  }
}


Sortable.reClassName = new RegExp ('(?:^| )(sort|sortAscending|sortDescending)(?: |$)'); 
Sortable.sort = function( thNode )
{
  var table = getAncestorByTagName(thNode, "TABLE" ); if( !table ) return;
  if( table.rows.length < 2 ) return;
  var colidx = 0; //target column to sort
  var thNodeClassName = this.ClassSort; //default column header classname
  
  //validate header row
  for( var i=0; i < table.rows[0].cells.length; i++ )
  {
    var c = table.rows[0].cells[i];
    if( c.nodeName != "TH" ) return;
    
    if( thNode == c ) 
    { 
      colidx = i; 
      if( Sortable.reClassName.test(c.className) ) thNodeClassName = RegExp.$1; 
    }
    else
    {
      c.className = c.className.replace(Sortable.reClassName, "" ) + " " + this.ClassSort ;
      c.title = this.TitleSort;
    }
  }
  
  //find body rows and guess data type of colidx
  var rows = new Array();
  var num  = true;
  var date = true;
  for( var i=1; i< table.rows.length; i++)
  {
    rows[i-1] = table.rows[i] ;
    var val = rows[i-1].cells[colidx].firstChild.nodeValue;
    if( num  ) num  = !isNaN( parseFloat( val ) ) ;    
    if( date ) date = !isNaN( Date.parse( val ) );    
  }
  var datatype = "string";
  if( num ) datatype = "num";
  if( date ) datatype = "date";

  //do the actual sorting
  if( thNodeClassName == this.ClassSort ) //first time sort of column table.sortCol == colidx ) 
  {
    rows.sort( Sortable.createCompare( colidx, datatype ) );
    thNodeClassName = this.ClassSortAscending;
    thNode.title    = this.TitleSortAscending; 
  }
  else
  { 
    rows.reverse(); 
    if( thNodeClassName == this.ClassSortAscending )
    {
      thNodeClassName = this.ClassSortDescending;
      thNode.title    = this.TitleSortDescending;
    }
    else
    {
      thNodeClassName = this.ClassSortAscending;
      thNode.title    = this.TitleSortDescending;
    }
  }
  thNode.className = thNode.className.replace(Sortable.reClassName, "") + " " + thNodeClassName ;
  
  //put the sorted table back into the document
  var frag = document.createDocumentFragment();
  for( var i=0; i < rows.length; i++ )
  {
    frag.appendChild( rows[i] );
  }
  table.appendChild( frag );
  
}


Sortable.convert = function( val, datatype )
{
  switch( datatype )
  {
    case "num"  : return parseFloat( val );
    case "date" : return new Date( Date.parse( val ) );
    default     : return val.toString();
  }
}


Sortable.createCompare = function( colidx, datatype )
{
  return function(row1, row2)
  {
    var val1 = Sortable.convert( row1.cells[colidx].firstChild.nodeValue, datatype );
    var val2 = Sortable.convert( row2.cells[colidx].firstChild.nodeValue, datatype );

    if      ( val1 < val2 ) { return -1; }
    else if ( val1 > val2 ) { return 1;  }
    else { return 0; }
  } ;
}

CSS definitions.#

There are only a few extras needed in the css sheet: the cursor is changed to a hand when your mouse moves over a clickable table header.

  • Column headers which are clicable, but not yet sorted, get the style .sort
  • Column headers, which are sorted ascending, get the style .sortAscending. This stylesheet will color these header like this .
  • Column headers, which are sorted descending, get the style .sortDescending. This stylesheet will color these header like this .
.sortable .sort           { cursor: pointer; }
.sortable .sortAscending  { cursor: pointer; background: #99FF99; }
.sortable .sortDescending { cursor: pointer; background: #FF9933; }

Here is an alternative set of stylesheet definitions which will display an up or down arrow depending on the sortAscending/sortDescending status of a column. It uses some .png in an images directory.

extravanilla-sortable.jpg

Actually, the image is used as background of the column headers, which gets altered when clicking the header. The power of css is amazing :-)

.sortable .sort,
.sortable .sortAscending,
.sortable .sortDescending { padding: 0 1.25em;
                            background-color: #f0f0f0;
                            background-repeat: no-repeat; 
                            background-position: 0.25em 50%; }
.sortable .sortAscending  { background-image: url(images/bulletDown.png); }
.sortable .sortDescending { background-image: url(images/bulletUp.png); }


Back to BrushedTemplate

Add new attachment

Only authorized users are allowed to upload new attachments.

List of attachments

Kind Attachment Name Size Version Date Modified Author Change note
png
SortHighlightFix.png 8.1 kB 1 29-Nov-2006 01:04 Ryan Sawatzky
jpg
extravanilla-sortable.jpg 19.7 kB 1 24-Oct-2005 22:56 DirkFrederickx
js
jspwiki-common.js 30.2 kB 1 14-Sep-2006 23:01 Joseph Schmigel
jpg
vanilla-sort.jpg 22.7 kB 1 24-Oct-2005 22:56 DirkFrederickx
« This particular version was published on 24-Oct-2005 22:55 by DirkFrederickx.