The Joy of Tables On Cows

On June 12th MooTools 1.2 was released, to great rejoicing. It’s a release that really sets MooTools apart with better Fx, more browser compatibility effort, and a jaw dropping element storage feature. That means it’s time to update the table sort script I’ve blogged here before (The Joy of a Minimal, Complete Javascript Table Sort and The Joy of an Optimized, Complete Javascript Table Sort). This release does more than just port, and also adds a few features:

  • Sorts more out of the box:
    • strings
    • numbers
    • decimal currency (12.34, 4.50)
    • dates (YYYY-MM-DD, YYYY-M-D)
    • relative dates (1 day ago, 38 years ago)
    • disk memory (1.75 MB, 34 KB, 8 TB)
  • Passes the matcher into the conversion_function for re-use.
  • Classes set for forward and reverse sorting th tags.
  • A “don’t sort” class.
  • It’s on github!
  • Integration with the brand new pagination library.

Oh yeah, and that. There’s now a pagination library supporting all the same things like expanding rows. It’ll do multiple pagination link areas and drop offset and cut-off numbers into the DOM. Let’s take a look.

The Old: Reviewing SortingTable

SortingTable doesn’t need any wonky DOM rewrite, that’s one of it’s best perks. Just use a thead and tbody, and make proper use of th.

<table cellpadding="0" cellspacing="0" id="sort_this">
  <thead>
    <tr>
      <th>a header</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>a value
    </tr>
    <tr>
      <td>another value</td>
    </tr>
  </tbody>
</table>

To sort this table you could use the most simplistic of initializations:

new SortingTable( 'sort_this' );

These are the default settings that are assumed:

new SortingTable( 'sort_table', {
  zebra: true,                        // Stripe the table, also on initialize
  paginator: false,                   // Pass a paginator object
  dont_sort_class: 'nosort',          // Class name on th's that don't sort
  forward_sort_class: 'forward_sort', // Class applied to forward sort th's
  reverse_sort_class: 'reverse_sort'  // Class applied to reverse sort th's
});

So if you wanted to not sort a given column, just add class=“nosort” and it’ll be ignored. You could also change that to any other class. The same goes for the classes forward_sort and reverse_sort.

The passing of conversion_matcher into the conversion_function makes it more DRY to use regex on a td for sort. Take a look at the date sorter:

      // YYYY-MM-DD, YYYY-m-d
      { matcher: /(\d{4})-(\d{1,2})-(\d{1,2})/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          cell = this.conversion_matcher.exec( cell );
          return new Date(parseInt(cell[1]), parseInt(cell[2], 10) - 1, parseInt(cell[3], 10));
        }
      },

We get to re-use that regex right in the conversion_function.

That’s it for incremental changes to SortingTable. PaginatingTable is the thing to introduce.

The New: Introducing PaginatingTable

To paginate a table you’ll need to add one DOM element:

<ul id="sort_table_pagination"></ul>

And then paginating the simple table provided above would look like:

new PaginatingTable( 'sort_table', 'sort_table_pagination' );

That’s the simplest way to go. A more complex pagination might involve two paginators as well as displaying offsets and cutoffs.

<ul id="sort_table_pagination"></ul>
Now showing items <span id="offset"></span> - <span id="cutoff"></span>
<table cellpadding="0" cellspacing="0" id="sort_this">
  <thead>
    <tr>
      <th>a header</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>a value
    </tr>
    <tr>
      <td>another value</td>
    </tr>
  </tbody>
</table>
<ul id="sort_table_bottom_pagination"></ul>

To start this up:

new PaginatingTable( 'sort_table', ['sort_table_pagination', 'sort_table_bottom_pagination'], {
  per_page: 3,         // Only 3 items per page, please
  current_page: 2,     // Start on page 2
  offset_el: 'offset', // Use this id for offset numbers
  cutoff_el: 'cutoff'  // And this id for cutoffs
});

Swank. This will paginate, but you don’t get any sorting or zebra striping. That’s still tied to SortingTable, and you’ll need to use them together.

PaginatingTable also provides update_pages on a given instance- so if you add rows to a table you can have the pagination update itself.

Will It Blend? SortingTable and PaginatingTable Together

SortingTable will accept a PaginatingTable object and use it to reset to the first page when you sort. Just pass it in:

new SortingTable( 'sort_table', {
  paginator: new PaginatingTable( 'sort_table', 'sort_table_pagination' )
});

If you have expanding rows, you’ll need to tell both SortingTable and PaginatingTable about it:

new SortingTable( 'sort_table', {
  details: true
  paginator: new PaginatingTable( 'sort_table', 'sort_table_pagination', { details: true } )
});

You can check out the example to see this in action, and get the javascript from github. A big thanks to all the people that wrote conversion functions and made tweaks to this script, and kept it crawling from an example into a more capable library. Enjoy!