Features
- Really easy to set up
- Can sort on load if a header is specified otherwise assumes that the first column is already sorted
- Keyboard accessible
- Adds
titleattributes automatically to eachth - CSS arrow to signify ascending or descending
Demo
| Name | Job | Age |
|---|---|---|
| Art Vandelay | Architect | 33 |
| H.E. Pennypacker | Wealthy industrialist, and philanthropist | 35 |
| Kel Varnsen | Importer/exporter of fine latex goods | 30 |
How to use
- Create a
tableusingtbodytags for content andthfor headers - Add
"sortable"class to the table - (Optional) Specify a
thto be sorted when the document is ready by adding the class"sorted"or"sorted desc"for descending
Download
sortable-table.min.jssortable-table.min.css
Code
var sortClass = 'sorted';
var descSortClass = 'desc';
$().ready(function () {
$('table.sortable').each(function (i, sortableTable) {
var sorted = false;
var ths = $(sortableTable).find('thead tr').children();
for (var i = 0; i < ths.length; i++) {
if ($(ths[i]).hasClass(sortClass)) {
$(ths[i]).toggleClass(descSortClass); // toggle so it sorts correct order
var rows = mergeSortTable($(sortableTable).find('tbody'), i);
rearrangeTable(sortableTable, rows, ths[i]);
sorted = true;
break;
}
}
$(sortableTable).find('th').each(function (j, th) {
if (!sorted && j == 0) { // Assume sorted by first column if class not present
$(th).addClass(sortClass);
}
$(this).attr('tabindex', '0');
$(this).attr('title', 'Sort by ' + $(this).html().toLowerCase() + ' column');
$('<span>').appendTo(th); // CSS sort arrow after header
$(th)
.click({ columnIndex: j }, triggerSort)
.keydown({ columnIndex: j }, triggerSort);
});
});
});
function triggerSort(event) {
if (event.type == 'click' || event.keyCode == 13) {
var table = $(this).closest('table')
var rows = mergeSortTable($(table).find('tbody'), event.data.columnIndex);
rearrangeTable(table, rows, this);
}
}
function appendSlideDown(toSelector, html) {
$(html)
.hide()
.appendTo($(toSelector))
.slideDown('fast');
}
function prependSlideDown(toSelector, html) {
$(html)
.hide()
.prependTo($(toSelector))
.slideDown('fast');
}
function rearrangeTable(table, rows, sortTh) {
var sortDesc = ($(sortTh).hasClass(sortClass) && !$(sortTh).hasClass(descSortClass));
$(rows).each(function (i, row) {
$(row).remove();
if (sortDesc) { // desc
$(row).prependTo(table);
} else { // asc
$(row).appendTo(table);
}
});
if ($(sortTh).hasClass(sortClass)) {
$(sortTh).toggleClass(descSortClass);
} else {
var ths = $(table).find('th');
$(ths).removeClass(sortClass);
$(ths).removeClass(descSortClass);
$(sortTh).addClass(sortClass);
}
}
function mergeSortTable(rows, col) { // column is 0-based
if (rows.length <= 1) {
return rows;
}
var left = [];
var right = [];
var middle = Math.floor(rows.length / 2);
$(rows).each(function (i, row) {
if (i < middle) {
left[i] = row;
} else {
right[i - middle] = row;
}
});
left = mergeSortTable(left, col);
right = mergeSortTable(right, col);
return merge(left, right, col);
}
function merge(left, right, col) {
var results = [];
while (left.length > 0 || right.length > 0) {
if (left.length > 0 && right.length > 0) {
if (rowSortValue(left[0], col) < rowSortValue(right[0], col)) {
results[results.length] = left[0];
left.shift();
} else {
results[results.length] = right[0];
right.shift();
}
} else if (left.length > 0) {
results[results.length] = left[0];
left.shift();
} else if (right.length > 0) {
results[results.length] = right[0];
right.shift();
}
}
return results;
}
function rowSortValue(row, column) {
return $($(row).find('td')[column]).html().toLowerCase();
}
table.sortable th:hover {
cursor:pointer;
}
table.sortable th > span {
position:relative;
top:-16px;
left:5px;
width:0px;
height:0px;
border-left:5px solid transparent;
border-right:5px solid transparent;
border-bottom:5px solid transparent;
}
table.sortable th.sorted > span {
border-bottom:10px solid #000;
}
table.sortable th.sorted.desc > span {
top:16px;
border-bottom:0;
border-top:10px solid #000;
}