Changes

  • In New v2.30.5, the output_formatContent callback data parameter now includes the column index and parsed value for the cell.
  • In v2.29.0, updated internal function parameters to make it easier to output all table data from the server without modifying the current table, or changing the pager size or page. See the modified output_callback code in the Output all data section.
  • In v2.28.5, triggering of the "outputTable" event multiple times in rapid succession (within 600 milliseconds) will now prevent the opening of multiple popups or cause mutliple downloads.
  • In v2.28.4,
  • In v2.27.0, the output_callback now includes a url parameter.

Requirements

  • This widget will only work in tablesorter version 2.8+ and jQuery version 1.7+.
  • To download a file with the set filename and extension, the server's Content-disposition needs to be set
  • This widget now uses a method available in modern browsers (IE10+) to download files without the need for server side modifications
  • Support in older browsers (IE9 and older) have not been throughly tested.

Features & Usage

  • This widget can output the table data to:
    • JSON
    • Array of arrays
    • Text with any separator (comma, tab, etc).
  • Rowspan and colspan are supported by this widget. Please take note of additional information in the "Rowspan and colspan" section below.
  • Working with Excel:
    • To save a file for Excel
      • Set the separator to a comma (,).
      • Save the file with a csv extension, then load it into Excel.
      • Or, save the file with a xls extension; but when loading the file in Excel, you will likely see a message stating that the file is corrupt. Continue loading and you will see the expected data placed into each cell.
      • Note including a BOM in the output_encoding option is no longer required!
    • To copy the data into Excel
      • Set the separator as a tab (\t).
      • Open the data in a popup window, then copy the contents to the clipboard.
      • Paste from the clipboard directly into a cell in Excel.
  • This demo uses Bootstrap to create the download button dropdown for each table allowing the user to choose the settings
    • Using Bootstrap started out relatively difficult... any click inside would close the dropdown.
    • There are likely better alternatives as the download button and popup are all completely customizable.
    • The download dropdown code is overly complicated because it is set up for two tables & allows modifying almost all of the options.
    • The only thing needed to output the table code (download or open a popup) is to trigger the following event after all of your widget options are set as desired:
      $table.trigger('outputTable');

Known Issues

  • Some versions of Safari do not fully support direct downloading, nor will you be able to save the file with the set file name; for more details please refer to this issue.

Credit

Demo


Search in the Discount column

Table showing filter & pager support

1 - 5 / 13 (13)
 
Rank
First Name
Last Name
Age
Total
Discount
Date
Rank First Name Last Name Age Total Discount Date
1Philip Aaron WongJohnson Sr Esq25$5.9522%Jun 26, 2004 7:22 AM
11Aaron"doc" Hibert12$2.995%Aug 21, 2009 12:21 PM
12Brandon ClarkHenry Jr51$42.2918%Oct 13, 2000 1:15 PM
111PeterParker28$9.9920%Jul 6, 2006 8:14 AM
21John "Robin"
Hood
33$19.9925%Dec 10, 2002 5:14 AM

Table showing output widget rowspan & colspan support

line
values
value1
value2
value3
1 1.1 1.2 1.3
1.4 1.5
2 2.1 2.2 2.3
2.4 2.5
3 3.1 3.2 3.3
3.4 3.5
4 4.1 4.2
4.3

Page Header

<!-- Tablesorter: required -->
<link href="css/theme.bootstrap.css" rel="stylesheet">
<script src="js/jquery-latest.min.js"></script>
<script src="js/jquery.tablesorter.js"></script>
<script src="js/jquery.tablesorter.widgets.js"></script>
<!-- pager -->
<link href="css/jquery.tablesorter.pager.css" rel="stylesheet">
<script src="js/jquery.tablesorter.pager.js"></script>
<!-- Ouput widget -->
<script src="js/parsers/parser-input-select.js"></script>
<script src="js/widgets/widget-ouput.js"></script>

CSS

table.tablesorter tbody tr.odd.checked td {
background: #8080c0;
color: #fff;
}
table.tablesorter tbody tr.even.checked td {
background: #a0a0e0;
color: #fff;
}

Script

$(function() {

$('.group1 table').tablesorter({
theme: 'bootstrap',
checkboxClass : 'checked', // default setting
widthFixed : true,
headerTemplate : '{content} {icon}',
widgets: ["zebra", "filter", "uitheme", "output"],
widgetOptions : {
filter_filteredRow : 'filtered',
filter_reset : '.group1 .reset',

output_separator : ',', // ',' 'json', 'array' or separator (e.g. ';')
output_ignoreColumns : [0], // columns to ignore [0, 1,... ] (zero-based index)
output_hiddenColumns : false, // include hidden columns in the output
output_includeFooter : true, // include footer rows in the output
output_includeHeader : true, // include header rows in the output
output_headerRows : false, // output all header rows (if multiple rows)
output_dataAttrib : 'data-name', // data-attribute containing alternate cell text
output_delivery : 'p', // (p)opup, (d)ownload
output_saveRows : 'f', // (a)ll, (v)isible, (f)iltered, jQuery filter selector (string only) or filter function
output_duplicateSpans: true, // duplicate output data in tbody colspan/rowspan
output_replaceQuote : '\u201c;', // change quote to left double quote
output_includeHTML : true, // output includes all cell HTML (except the header cells)
output_trimSpaces : false, // remove extra white-space characters from beginning & end
output_wrapQuotes : false, // wrap every cell output in quotes
output_popupStyle : 'width=580,height=310',
output_saveFileName : 'mytable.csv',
// callback executed after the content of the table has been processed
output_formatContent : function(config, widgetOptions, data) {
// data.isHeader (boolean) = true if processing a header cell
// data.$cell = jQuery object of the cell currently being processed
// data.content = processed cell content (spaces trimmed, quotes added/replaced, etc)
// data.columnIndex = column in which the cell is contained
// data.parsed = cell content parsed by the associated column parser
return data.content;
},
// callback executed when processing completes
output_callback : function(config, data, url) {
// return false to stop delivery & do something else with the data
// return true OR modified data (v2.25.1) to continue download/output
return true;
},
// callbackJSON used when outputting JSON & any header cells has a colspan - unique names required
output_callbackJSON : function($cell, txt, cellIndex) {
return txt + '(' + cellIndex + ')';
},
// the need to modify this for Excel no longer exists
output_encoding : 'data:application/octet-stream;charset=utf8,',
// override internal save file code and use an external plugin such as
// https://github.com/eligrey/FileSaver.js
output_savePlugin : null /* function(config, widgetOptions, data) {
var blob = new Blob([data], {type: widgetOptions.output_encoding});
saveAs(blob, widgetOptions.output_saveFileName);
} */
}
}).tablesorterPager({
container: $('.ts-pager'),
output: '{startRow} - {endRow} / {filteredRows} ({totalRows})',
size: 5
});

$('.group2 table').tablesorter({
theme: 'bootstrap',
widthFixed : true,
headerTemplate : '{content} {icon}',
widgets: ["zebra", "filter", "uitheme", "output"],
widgetOptions : {
filter_filteredRow : 'filtered',
filter_reset : '.group2 .reset',
output_headerRows : true, // output all header rows (multiple rows)
output_popupStyle : 'width=580,height=310',
output_saveFileName : 'mytable.csv'
}
});

// set up download buttons for two table groups
var demos = ['.group1', '.group2'];

$.each(demos, function(groupIndex) {
var $this = $(demos[groupIndex]);

$this.find('.dropdown-toggle').click(function(e) {
// this is needed because clicking inside the dropdown will close
// the menu with only bootstrap controlling it.
$this.find('.dropdown-menu').toggle();
return false;
});
// make separator & replace quotes buttons update the value
$this.find('.output-separator').click(function() {
$this.find('.output-separator').removeClass('active');
var txt = $(this).addClass('active').html();
$this.find('.output-separator-input').val( txt );
$this.find('.output-filename').val(function(i, v) {
// change filename extension based on separator
var filetype = (txt === 'json' || txt === 'array') ? 'js' :
txt === ',' ? 'csv' : 'txt';
return v.replace(/\.\w+$/, '.' + filetype);
});
return false;
});
$this.find('.output-quotes').click(function() {
$this.find('.output-quotes').removeClass('active');
$this.find('.output-replacequotes').val( $(this).addClass('active').text() );
return false;
});
// header/footer toggle buttons
$this.find('.output-header, .output-footer').click(function() {
$(this).toggleClass('active');
});
// clicking the download button; all you really need is to
// trigger an "output" event on the table
$this.find('.download').click(function() {
var typ,
$table = $this.find('table'),
wo = $table[0].config.widgetOptions,
val = $this.find('.output-filter-all :checked').attr('class');
wo.output_saveRows = val === 'output-filter' ? 'f' :
val === 'output-visible' ? 'v' :
// checked class name, see table.config.checkboxClass
val === 'output-selected' ? '.checked' :
val === 'output-sel-vis' ? '.checked:visible' :
'a';
val = $this.find('.output-download-popup :checked').attr('class');
wo.output_delivery = val === 'output-download' ? 'd' : 'p';
wo.output_separator = $this.find('.output-separator-input').val();
wo.output_replaceQuote = $this.find('.output-replacequotes').val();
wo.output_trimSpaces = $this.find('.output-trim').is(':checked');
wo.output_includeHTML = $this.find('.output-html').is(':checked');
wo.output_wrapQuotes = $this.find('.output-wrap').is(':checked');
wo.output_saveFileName = $this.find('.output-filename').val();

// first example buttons, second has radio buttons
if (groupIndex === 0) {
wo.output_includeHeader = $this.find('button.output-header').is(".active");
} else {
wo.output_includeHeader = !$this.find('.output-no-header').is(':checked');
wo.output_headerRows = $this.find('.output-headers').is(':checked');
}
// footer not included in second example
wo.output_includeFooter = $this.find('.output-footer').is(".active");

$table.trigger('outputTable');
return false;
});

// add tooltip
$this.find('.dropdown-menu [title]').tipsy({ gravity: 's' });

});

});

HTML

<div class="group1">
<h4>Table showing filter & pager support</h4>
<div class="btn-group">
<button type="button" class="btn btn-default reset">Reset</button> <!-- targeted by the "filter_reset" option -->

<!-- Split button -->
<div class="btn-group">
<button type="button" class="btn btn-default download">Output</button>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu" role="menu">
<li><h5><strong>Output options</strong></h5></li>
<li>
<label>Separator: <input class="output-separator-input" type="text" size="2" value=","></label>
<button type="button" class="output-separator btn btn-default btn-xs active" title="comma">,</button>
<button type="button" class="output-separator btn btn-default btn-xs" title="semi-colon">;</button>
<button type="button" class="output-separator btn btn-default btn-xs" title="tab"></button>
<button type="button" class="output-separator btn btn-default btn-xs" title="space"></button>
<button type="button" class="output-separator btn btn-default btn-xs" title="output JSON">json</button>
<button type="button" class="output-separator btn btn-default btn-xs" title="output Array (see note)">array</button>
</li>
<li>
<label>Send to:</label>
<div class="btn-group output-download-popup" data-toggle="buttons" title="Download file or open in Popup window">
<label class="btn btn-default btn-sm active">
<input type="radio" name="delivery1" class="output-popup" checked=""> Popup
</label>
<label class="btn btn-default btn-sm">
<input type="radio" name="delivery1" class="output-download"> Download
</label>
</div>
</li>
<li>
<label>Include:</label>
<div class="btn-group output-filter-all" data-toggle="buttons" title="Output only filtered, visible, selected, selected+visible or all rows">
<label class="btn btn-default btn-sm active">
<input type="radio" name="getrows1" class="output-filter" checked="checked"> Filtered
</label>
<label class="btn btn-default btn-sm">
<input type="radio" name="getrows1" class="output-visible"> Visible
</label>
<label class="btn btn-default btn-sm">
<input type="radio" name="getrows1" class="output-selected"> Selected
</label>
<label class="btn btn-default btn-sm">
<input type="radio" name="getrows1" class="output-sel-vis"> Sel+Vis
</label>
<label class="btn btn-default btn-sm">
<input type="radio" name="getrows1" class="output-all"> All
</label>
</div>
</li>
<li>
<button class="output-header btn btn-default btn-sm active" title="Include table header">Header</button>
<button class="output-footer btn btn-default btn-sm active" title="Include table footer">Footer</button>
</li>
<li class="divider"></li>
<li>
<label>Replace quotes: <input class="output-replacequotes" type="text" size="2" value="'"></label>
<button type="button" class="output-quotes btn btn-default btn-xs active" title="single quote">'</button>
<button type="button" class="output-quotes btn btn-default btn-xs" title="left double quote"></button>
<button type="button" class="output-quotes btn btn-default btn-xs" title="escaped quote">\"</button>
</li>
<li><label title="Remove extra white space from each cell">Trim spaces: <input class="output-trim" type="checkbox" checked=""></label></li>
<li><label title="Include HTML from cells in output">Include HTML: <input class="output-html" type="checkbox"></label></li>
<li><label title="Wrap all values in quotes">Wrap in Quotes: <input class="output-wrap" type="checkbox"></label></li>
<li><label title="Choose a download filename">Filename: <input class="output-filename" type="text" size="15" value="mytable.csv"></label></li>
</ul>
</div>
</div>

<div class="ts-pager form-horizontal">
<button type="button" class="btn btn-default btn-sm first"><i class="glyphicon glyphicon-step-backward"></i></button>
<button type="button" class="btn btn-default btn-sm prev"><i class="glyphicon glyphicon-backward"></i></button>
<span class="pagedisplay"></span> <!-- this can be any element, including an input -->
<button type="button" class="btn btn-default btn-sm next"><i class="glyphicon glyphicon-forward"></i></button>
<button type="button" class="btn btn-default btn-sm last"><i class="glyphicon glyphicon-step-forward"></i></button>
<select class="pagesize form-control btn-sm" title="Select page size">
<option selected="selected" value="5">5</option>
<option value="10">10</option>
</select>
<select class="gotoPage form-control btn-sm" title="Select page number"></select>
</div>

<table id="table" class="tablesorter">
<thead>
<tr>
<th class="sorter-checkbox filter-false"> </th>
<th>Rank</th>
<th data-name="First">First Name</th>
<th data-name="Last">Last Name</th>
<th>Age</th>
<th>Total</th>
<th>Discount</th>
<th>Date</th>
</tr>
</thead>
<tfoot>
<tr>
<th></th>
<th>Rank</th>
<th data-name="First & middle">First Name</th>
<th data-name="Last & Title">Last Name</th>
<th>Age</th>
<th>Total</th>
<th>Discount</th>
<th>Date</th>
</tr>
</tfoot>
<tbody>
<tr><td><input type="checkbox"></td><td>1</td><td>Philip Aaron Wong</td><td>Johnson Sr Esq</td><td>25</td><td>$5.95</td><td>22%</td><td>Jun 26, 2004 7:22 AM</td></tr>
<tr><td><input type="checkbox"></td><td>11</td><td>Aaron</td><td>"doc" Hibert</td><td>12</td><td>$2.99</td><td>5%</td><td>Aug 21, 2009 12:21 PM</td></tr>
<tr><td><input type="checkbox"></td><td>12</td><td>Brandon Clark</td><td>Henry Jr</td><td>51</td><td>$42.29</td><td>18%</td><td>Oct 13, 2000 1:15 PM</td></tr>
<tr><td><input type="checkbox"></td><td>111</td><td>Peter</td><td>Parker</td><td>28</td><td>$9.99</td><td>20%</td><td>Jul 6, 2006 8:14 AM</td></tr>
<tr><td><input type="checkbox"></td><td>21</td><td><span class="red">John</span></td><td> "Robin"<br> Hood </td><td>33</td><td>$19.99</td><td>25%</td><td>Dec 10, 2002 5:14 AM</td></tr>
<tr><td><input type="checkbox"></td><td>013</td><td>Clark</td><td>"Old man" Kent Sr.</td><td>18</td><td>$15.89</td><td>44%</td><td>Jan 12, 2003 11:14 AM</td></tr>
<tr><td><input type="checkbox"></td><td>005</td><td>Bruce</td><td>Almighty <span class="red">Esq</span></td><td>45</td><td>$153.19</td><td>44%</td><td>Jan 18, 2021 9:12 AM</td></tr>
<tr><td><input type="checkbox"></td><td>10</td><td>Alex</td><td>Dumass</td><td>13</td><td>$5.29</td><td>4%</td><td>Jan 8, 2012 5:11 PM</td></tr>
<tr><td><input type="checkbox"></td><td>16</td><td>Jim</td><td>"Jimmy" Franco</td><td>24</td><td><span class="red">-$14.19</span></td><td>24%</td><td>Jan 14, 2004 11:23 AM</td></tr>
<tr><td><input type="checkbox"></td><td>166</td><td>Bruce Lee</td><td>Evans</td><td>22</td><td>$13.19</td><td>11%</td><td>Jan 18, 2007 9:12 AM</td></tr>
<tr><td><input type="checkbox"></td><td>100</td><td>Brenda Dexter</td><td>McMasters</td><td>18</td><td>$55.20</td><td>15%</td><td>Feb 12, 2010 7:23 PM</td></tr>
<tr><td><input type="checkbox"></td><td>55</td><td>Dennis</td><td><span class="red">"Charley"</span> Bronson</td><td>65</td><td>$123.00</td><td>32%</td><td>Jan 20, 2001 1:12 PM</td></tr>
<tr><td><input type="checkbox"></td><td>9</td><td>Martha</td><td>delFuego</td><td>25</td><td>$22.09</td><td>12%</td><td>Jun 11, 2011 10:55 AM</td></tr>
</tbody>
</table>
</div>

<div class="group2">
<h4>Table showing output widget rowspan & colspan support</h4>
<div class="btn-group">
<button type="button" class="btn btn-default reset">Reset</button> <!-- targeted by the "filter_reset" option -->

<!-- Split button -->
<div class="btn-group">
<button type="button" class="btn btn-default download">Output</button>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu" role="menu">
<li><h5><strong>Output options</strong></h5></li>
<li>
<label>Separator: <input class="output-separator-input" type="text" size="2" value=","></label>
<button type="button" class="output-separator btn btn-default btn-xs active" title="comma">,</button>
<button type="button" class="output-separator btn btn-default btn-xs" title="semi-colon">;</button>
<button type="button" class="output-separator btn btn-default btn-xs" title="tab"></button>
<button type="button" class="output-separator btn btn-default btn-xs" title="space"></button>
<button type="button" class="output-separator btn btn-default btn-xs" title="output JSON">json</button>
<button type="button" class="output-separator btn btn-default btn-xs" title="output Array (see note)">array</button>
</li>
<li>
<label>Send to:</label>
<div class="btn-group output-download-popup" data-toggle="buttons" title="Download file or open in Popup window">
<label class="btn btn-default btn-sm active">
<input type="radio" name="delivery2" class="output-popup" checked=""> Popup
</label>
<label class="btn btn-default btn-sm">
<input type="radio" name="delivery2" class="output-download"> Download
</label>
</div>
</li>
<li>
<label>Include:</label>
<div class="btn-group output-filter-all" data-toggle="buttons" title="Output only filtered, visible or all rows">
<label class="btn btn-default btn-sm active">
<input type="radio" name="getrows2" class="output-filter" checked=""> Filtered
</label>
<label class="btn btn-default btn-sm">
<input type="radio" name="getrows2" class="output-visible"> Visible
</label>
<label class="btn btn-default btn-sm">
<input type="radio" name="getrows2" class="output-all"> All
</label>
</div>
</li>
<li>
<label>Header:</label>
<div class="btn-group output-includes" data-toggle="buttons" title="Include no, single (last row), or all table headers">
<label class="btn btn-default btn-sm">
<input type="radio" name="headers" class="output-no-header"> None
</label>
<label class="btn btn-default btn-sm">
<input type="radio" name="headers"> Single
</label>
<label class="btn btn-default btn-sm active">
<input type="radio" name="headers" class="output-headers" checked=""> All
</label>
</div>
</li>
<li class="divider"></li>
<li>
<label>Replace quotes: <input class="output-replacequotes" type="text" size="2" value="'"></label>
<button type="button" class="output-quotes btn btn-default btn-xs active" title="single quote">'</button>
<button type="button" class="output-quotes btn btn-default btn-xs" title="left double quote"></button>
<button type="button" class="output-quotes btn btn-default btn-xs" title="escaped quote">\"</button>
</li>
<li><label title="Remove extra white space from each cell">Trim spaces: <input class="output-trim" type="checkbox" checked=""></label></li>
<li><label title="Include HTML from cells in output">Include HTML: <input class="output-html" type="checkbox"></label></li>
<li><label title="Wrap all values in quotes">Wrap in Quotes: <input class="output-wrap" type="checkbox"></label></li>
<li><label title="Choose a download filename">Filename: <input class="output-filename" type="text" size="15" value="mytable.csv"></label></li>
</ul>
</div>

</div>

<table id="table2">
<thead>
<tr>
<th rowspan="2">line</th>
<th colspan="3">values</th>
</tr>
<tr>
<th>value1</th>
<th>value2</th>
<th>value3</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="2">1</td>
<td>1.1</td>
<td>1.2</td>
<td><span class="red">1.3</span></td>
</tr>
<tr class="tablesorter-childRow">
<td>1.4</td>
<td colspan="2">1.5</td>
</tr>
<tr>
<td rowspan="2">2</td>
<td>2.1</td>
<td><span class="red">2.2</span></td>
<td>2.3</td>
</tr>
<tr class="tablesorter-childRow">
<td>2.4</td>
<td colspan="2">2.5</td>
</tr>
<tr>
<td rowspan="2">3</td>
<td>3.1</td>
<td>3.2</td>
<td>3.3</td>
</tr>
<tr class="tablesorter-childRow">
<td>3.4</td>
<td colspan="2"><span class="red">3.5</span></td>
</tr>
<tr>
<td rowspan="2">4</td>
<td rowspan="2" colspan="2"><span class="red">4.1</span></td>
<td>4.2</td>
</tr>
<tr class="tablesorter-childRow">
<td>4.3</td>
</tr>
</tbody>
</table>
</div>