Client Side Table Sorting in JavaScript

Table sorting is a very common polishing feature that you’ll see for most tabular data. In ASP.NET you get functionality with this out of the box with a control like a GridView, but requires a full postback just to sort data that is already presented on the screen (unless your in an update panel – but even still, seems like a lot of work when you have all the data). That is when client side table sorting enters the picture. This makes for an especially nice user experience and reduces development time in worrying about having to sort data server side. This also reduces load on your server by minimizing the number of postbacks required.

In the end there are a ton of reasons to use client side sorting in Javascript if you are able to do so (assuming the data is not required to be paged at all). The easiest method I have run into is using a JQuery plugin called “Tablesorter” (very original J). This plugin is great, simple to use, and provides exactly what you are looking for with a couple of bells and whistles.

The JQuery Tablesorter can be found at: http://tablesorter.com/docs/

The documentation suggests its compatibility with v1.2 of JQuery. I have personally confirmed it works in JQuery 1.3.2 and 1.4.2 (the newest version). So no worries with those versions.

The first thing in working with the Tablesorter is to use proper compliant html in your tables. This means making use of the thead, tbody, and tfoot nodes when creating your data. The Tablesorter will recognize these table types automatically. Here is an example of proper markup:

  1.  
  2. <table id="example1" border="0">
  3. <thead>
  4. <tr class="headerSort">
  5. <th>Column 1</th>
  6. <th>Column 2</th>
  7. <th>Column 3</th>
  8. <th>Column 4</th>
  9. <th>Column 5</th>
  10. </tr>
  11. </thead>
  12. <tbody>
  13. <tr>
  14. <td>Data 11</td>
  15. <td>Data 12</td>
  16. <td>Data 13</td>
  17. <td>Data 14</td>
  18. <td>Data 15</td>
  19. </tr>
  20. </tbody>
  21. <tfoot>
  22. <tr>
  23. <td>Foot 66</td>
  24. <td>Foot 66</td>
  25. <td>Foot 66</td>
  26. <td>Foot 66</td>
  27. <td>Foot 66</td>
  28. </tr>
  29. </tfoot></table>
  30.  

<thead> – This will be recognized as being a clickable cell that will sort the rows beneath it.

<tbody> – This will be recognized as rows that are ready to be sorted.

<tfoot> – This will be recognized as rows that should not be sorted – i.e. footer row for grand total, etc.

After that, you need to import two scripts: JQuery core, and the Tablesorter script. Then, you need to initialize the Tablesorter for the table you created in HTML. This is super easy:

  1. $(document).ready(defaultSorter);
  2.         function defaultSorter() {
  3.             $("#example1").tablesorter();
  4.         }

And that is it. You have implemented the base functionality of table sorting. This will automatically recognize and determine data types as Numeric or Text. When a user clicks on a header it will automatically change its styles based on asc, desc, or not sorted. You can of course add some styles to these to give visual aid to the user on what he has clicked to sort. These styles should be self explanatory:

  1. table td { padding: 5px; }
  2. .headerSort th
  3. {
  4.     background: url(Images/sortable.gif) no-repeat center right;
  5.     color: Red; padding-right: 10px; cursor: pointer;
  6.  
  7. }
  8. th.headerSortUp { background-image: url(Images/asc.gif); }
  9. th.headerSortDown { background-image: url(Images/desc.gif); }

Now you should have a table sorting correctly that looks like the following:

TableImage

However, if you have some type of numeric data hidden within nodes or perhaps, “80 km” or a measurement like “80 cm”, this data type will be auto detected as text. In order for it to sort correctly you have to write a custom parser and tell the Tablesorter to use it. Check out this example:

  1. $(document).ready(SorterWithDefinedParsers);
  2. function SorterWithDefinedParsers() {
  3.     // add custom parser
  4.     $.tablesorter.addParser({
  5.         // set a unique id
  6.         id: ‘dataTag’,
  7.         is: function(s) {
  8.             // return false so this parser is not auto detected
  9.             return false;
  10.         },
  11.         format: function(s) {
  12.             // format your data for normalization
  13.             var n = 0;
  14.             if (s.indexOf(‘Data ‘) > -1) {
  15.                 n = s.substring(5,s.length);
  16.             }
  17.  
  18.             return n;
  19.         },
  20.         // set type, either numeric or text
  21.         type: ‘numeric’
  22.     });
  23.  
  24.     // declare table
  25.     $("#example1").tablesorter({
  26.         headers: {
  27.             // this column is a standard text column.
  28.             // I could not even include this column,
  29.             // which would allow it to auto detect.
  30.             0: {
  31.                 sorter: ‘text’
  32.             },
  33.             // this column uses our custom parser
  34.             1: {
  35.                 sorter: ‘dataTag’
  36.             },
  37.             // disabled the sorting completely on this column.
  38.             2: {
  39.                 sorter: false
  40.             }
  41.         },
  42.         sortList: [[0, 0]] // this is the default sort
  43.     });
  44. }

There are a couple of things going on in the above example. First, we add a new parser to the Tablesorter repertoire. Then we initialize the Tablesorter headers by column number (starting at 0). You simply specify the Tablesorter parser to use. Optionally, you can specify “false” and this will prevent sorting on that column. The final “sortList” property specify’s the default sorting column and secondly the order (0=asc, 1= desc).

Hmmmm, not bad, however we can clean this up a bit and get Tablesorter to do some of the work for us by using a text extraction option as well. In text extraction you specify how the text comes out of the cell and returned. Take a look:

  1. $(document).ready(SorterWithTextExtraction);
  2. function SorterWithTextExtraction() {
  3.     // declare table
  4.     $("#example1").tablesorter({
  5.         textExtraction: function(node) {
  6.             // take out any spaces
  7.             s = $(node).html().replace(/ /g, );
  8.  
  9.             // remove anything after the |
  10.             var n = 0;
  11.             if (s.indexOf(‘Data’) > -1) {
  12.                 n = s.substring(4, s.length);
  13.             }
  14.             else {
  15.                 n = s;
  16.             }
  17.  
  18.             // now return to sort on this numeric
  19.             return n;
  20.         }
  21.     });
  22. }

This is nice since then you don’t have to specify each columns data table. You could potentially have a text extraction method that looked for and removed things like “KM”, “CM”, “%”, anything you wanted. Then when a new column is added to your table it is automatically sorted without the requirement of you to change the code. I like using textExtraction where possible, but for where you need further granularity or specialized business logic you can use the custom parsers above.

Integration with ASP.NET

This Tablesorter is simple to work with, and easy to integrate into .NET with a gridview for example. In the example of using a GridView, the only concern is making sure your column headers end up in  a <thead> section. The GridView does not do that out of the box. You can do this though by adjusting the GridView rendering something like this:

ReportGrid.HeaderRow.TableSection = TableRowSection.TableHeader;  

Where, in the above line, the ReportGrid is your GridView.

Conclusion

Download JQuery: www.jquery.com

Download Tablesorter: www.tablesorter.com

There are lot more options on what you can do with the Tablesorter, including attaching meta data to drive it, programmatically sorting it from javaScript (if you want to add an external source to sort – like from a button, etc). Be sure to check out the website for the details.

Download the Full VS 2008 Solution Example