<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>
Table Sorter Test Page
</title>
</head>
<body>
<h1>Table Sorter Test Page</h1>
<h2>Table with multiple types</h2>
<table>
<tr>
<th>Name</th>
<th>Number</th>
<th>Number</th>
<th>Number</th>
<th>Float</th>
<th>Date</th>
<th>Time</th>
<th>Datetime</th>
<th>Currency - $</th>
<th>Currency - €</th>
<th>Percentage</th>
<th>HTML</th>
<th>Progress</th>
</tr>
<tr>
<td>Bob</td>
<td>5</td>
<td>111,000</td>
<td>111 000</td>
<td>10.5</td>
<td>August 16, 2003</td>
<td>5:10</td>
<td>August 16, 2003 9:10</td>
<td>$111</td>
<td>€111</td>
<td>5%</td>
<td><strike>Bob</strike></td>
<td><progress value="5" max="100">5%</progress></td>
</tr>
<tr>
<td>Jim</td>
<td>0</td>
<td>2,000</td>
<td>2 000</td>
<td>10.0</td>
<td>January 22, 2003</td>
<td>0:33</td>
<td>August 16, 2003 1:33</td>
<td>$2k</td>
<td>€2M</td>
<td>0%</td>
<td><marquee>Jim</marquee></td>
<td><progress value="0" max="100">0%</progress></td>
</tr>
<tr>
<td>Arthur</td>
<td>77</td>
<td>3,000</td>
<td>3 000</td>
<td>10.77</td>
<td>January 7, 2003</td>
<td>22:22</td>
<td>January 7, 2003 22:22</td>
<td>$30</td>
<td>€30</td>
<td>77%</td>
<td><i>Arthur</i></td>
<td><progress value="77" max="100">77%</progress></td>
</tr>
<tr>
<td>Zuri</td>
<td>111</td>
<td>400</td>
<td>400</td>
<td>10.111</td>
<td>May 1, 1985</td>
<td>11:00</td>
<td>August 16, 2003 21:15</td>
<td>$9</td>
<td>€9</td>
<td>111%</td>
<td><b>Zuri</b></td>
<td><progress value="100" max="100">100%</progress></td>
</tr>
<tr>
<td>-</td>
<td>-</td>
<td>-</td>
<td>n/a</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
</table>
<h2>Table with range values</h2>
<style>
.bordered{
filter: drop-shadow(6px 4px 4px #777777);
}
.bold{
font-weight:bold;
}
</style>
<table class="bordered">
<tbody>
<tr>
<td>1</td>
<td>-5</td>
</tr>
<tr>
<td>2</td>
<td>-4</td>
</tr>
<tr>
<td>3</td>
<td>-3</td>
</tr>
<tr>
<td>4</td>
<td>-2</td>
</tr>
<tr>
<td style="font-weight:bold;">5</td>
<td class="bold">-1</td>
</tr>
<tr>
<td>6</td>
<td>0</td>
</tr>
<tr>
<td>7</td>
<td>1</td>
</tr>
<tr>
<td>8</td>
<td>2</td>
</tr>
<tr>
<td>9</td>
<td>3</td>
</tr>
<tr>
<td>10</td>
<td>4</td>
</tr>
</tbody>
</table>
<h2>Table with double header</h2>
<table>
<thead>
<tr>
<th colspan="2">Rebounds</th>
</tr>
<tr>
<th>Name</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tr>
<td>Zuri</td>
<td>1</td>
</tr>
<tr>
<td>Mike</td>
<td>2</td>
</tr>
<tr>
<td>Bob</td>
<td>3</td>
</tr>
</tbody>
</table>
<h2>Table with thead, tbody, tfoot</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tr>
<td>Zuri</td>
<td>1</td>
</tr>
<tr>
<td>Mike</td>
<td>2</td>
</tr>
<tr>
<td>Bob</td>
<td>3</td>
</tr>
<tr>
<td>Jim</td>
<td>4</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>Name</th>
<th>Number</th>
</tr>
</tfoot>
</table>
<h2>Table without thead, tbody, tfoot</h2>
<table>
<tr>
<th>Name</th>
<th>Number</th>
</tr>
<tr>
<td>Zuri</td>
<td>-3</td>
</tr>
<tr>
<td>Mike</td>
<td>2</td>
</tr>
<tr>
<td>Bob</td>
<td>11</td>
</tr>
</table>
<h2>Table with thead, but no td</h2>
<table>
<thead>
<tr>
<td>Name</td>
<td>Number</td>
</tr>
</thead>
<tr>
<td>Zuri</td>
<td>-3</td>
</tr>
<tr>
<td>Mike</td>
<td>2</td>
</tr>
<tr>
<td>Bob</td>
<td>11</td>
</tr>
</table>
<h2>Table without thead, tbody, tfoot and even without th</h2>
<table>
<tr>
<td>Zuri</td>
<td>-3</td>
</tr>
<tr>
<td>Mike</td>
<td>2</td>
</tr>
<tr>
<td>Bob</td>
<td>11</td>
</tr>
</table>
</body>
</html>
javascript:(function(){
/* Smart table sort bookmarklet + conditional formatting */
/* Author: Vilius L.
/* Based on https://github.com/HubSpot/sortable */
var addEventListener, clickEvents, numberRegExp, sortable, trimRegExp;
numberRegExp = /^-?[£$¤]?[\d,.]+%?$/;
trimRegExp = /^\s+|\s+$/g;
clickEvents = ['click'];
addEventListener = function(el, event, handler) {
if (el.addEventListener != null) {
return el.addEventListener(event, handler, false);
} else {
return el.attachEvent("on" + event, handler);
}
};
sortable = {
prepare: function(){
/*inject CSS*/
var CSS = `
table.sortable th{
cursor: pointer;
}
table.sortable th[data-sorted-direction="ascending"],
table.sortable th[data-sorted-direction="descending"]{
position:relative;
padding-right: 20px !important;
}
table.sortable th[data-sorted-direction="ascending"]:after,
table.sortable th[data-sorted-direction="descending"]:after{
position: absolute;
content: '';
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
top: calc(50% - 4px);
right: 4px;
}
table.sortable th[data-sorted-direction="ascending"]:after {
border-bottom: 8px solid #555;
}
table.sortable th[data-sorted-direction="descending"]:after{
border-top: 8px solid #555;
}
table.sortable td {
background-color: white;
}
table.sortable tr:nth-child(2n) td {
background-color: #f3f7fa;
}
`;
const style = document.createElement('style');
style.textContent = CSS;
document.head.append(style);
/*prepare tables - fix "thead td" to "thead th"*/
var tables = document.querySelectorAll('table');
for(var i = 0; i < tables.length; i++) {
var tables_thead_td = tables[i].querySelectorAll('thead td');
if(tables_thead_td.length > 0){
tables_thead_td[0].parentNode.innerHTML = tables_thead_td[0].parentNode.innerHTML
.replace(/<td/gi, '<th')
.replace(/<\/td>/gi, '</th>');
}
}
/*prepare tables - drop thead with rowspan*/
var tables = document.querySelectorAll('table thead tr');
for(var i = 0; i < tables.length; i++) {
var table_errors = tables[i].querySelectorAll('th[colspan]');
if(table_errors.length > 0){
tables[i].parentNode.removeChild(tables[i]);
}
}
/*prepare tables without thead and th"*/
var tables = document.querySelectorAll('table');
for(var i = 0; i < tables.length; i++) {
var table_thead = tables[i].querySelectorAll('thead');
if(table_thead.length > 0)
continue;
var table_th = tables[i].querySelectorAll('th');
if(table_th.length > 0)
continue;
var table_row = tables[i].querySelectorAll('tr');
if(table_row.length == 0)
continue;
var thead = document.createElement('thead');
var table__thead = tables[i].appendChild(thead);
var tr = document.createElement('tr');
var tr_object = table__thead.appendChild(tr);
var table_rows = table_row[0].querySelectorAll('td');
for(var j = 0; j < table_rows.length; j++) {
var header_name = "#" + (j + 1);
tr_object.appendChild(document.createElement("th")).
appendChild(document.createTextNode(header_name));
}
}
/*If there’s no tHead but the first tBody row contains ths, create a tHead and move that row into it.*/
var firstTBodyRow, tHead;
var tables = document.querySelectorAll('table');
for(var i = 0; i < tables.length; i++) {
var table = tables[i];
if (!table.tHead && (firstTBodyRow = table.tBodies[0].rows[0]).children[0].tagName === 'TH') {
tHead = document.createElement('thead');
tHead.appendChild(firstTBodyRow);
table.insertBefore(tHead, table.firstChild);
}
}
},
highlight:function(){
parseNumber = function(a) {
a = a.toString()
.replace(/\u20ac/g, '') /* dollar */
.replace(/\u0024/g, '') /* euro */
.replace(/%/g, '')
.replace(/,/g, '')
.replace(/ /g, '')
.replace(/:/g, '.')
.replace(/\u00A0/g, ''); /* */
if(a.toLowerCase().indexOf('k') > -1 && a.replace(/k$/ig, '').match(numberRegExp)){
a = (parseFloat(a.replace(/k$/ig, '')) * 1000).toString();
}
else if(a.toLowerCase().indexOf('m') > -1 && a.replace(/m$/ig, '').match(numberRegExp)){
a = (parseFloat(a.replace(/m$/ig, '')) * 1000 * 1000).toString();
}
return a;
};
var tables = document.querySelectorAll('table');
for(var i = 0; i < tables.length; i++) {
/*tables*/
var numeric_cols_map = [];
var table_data = [];
var rows = tables[i].querySelectorAll('tbody tr');
var rows_count = rows.length;
if(rows_count < 1)
continue;
for(var j = 0; j < rows.length; j++) {
/*rows*/
table_data[j] = [];
var cells = rows[j].querySelectorAll('td');
for(var k = 0; k < cells.length; k++) {
/*cells*/
table_data[j][k] = cells[k];
var value = cells[k].innerText;
var value = parseNumber(value);
if(value == '' || value == '-' || value.toLowerCase() == 'n/a') value = '0';
if(typeof numeric_cols_map[k] == 'undefined')
numeric_cols_map[k] = 1;
if(!value.match(numberRegExp)){
/*mark row as not numeric*/
numeric_cols_map[k] = 0;
}
}
}
/* we have wanted cols */
for(var j = 0; j < numeric_cols_map.length; j++) {
if(numeric_cols_map[j] == 0)
continue;
/* find min and max */
var min = null;
var max = null;
for(var k = 0; k < rows_count; k++) {
var value = this.getNodeValue(table_data[k][j]);
value = parseNumber(value);
if(value == '' || value == '-' || value.toLowerCase() == 'n/a')
continue;
value = parseFloat(value);
if(value < min || min === null) min = value;
if(value > max || max === null) max = value;
}
if(min == max)
continue;
/*loop again and set color*/
for(var k = 0; k < rows_count; k++) {
var node = table_data[k][j];
var value = this.getNodeValue(table_data[k][j]);
var value = parseNumber(value);
if(value == '' || value == '-' || value.toLowerCase() == 'n/a')
continue;
value = parseFloat(value);
var delta = (value - min) * 100 / (max - min); /* in range of [0 - 100] */
var exp_level = 4; /* fading average range */
var total_fade_level = 1.5; /* is colors too strong? */
if(delta > 50){
delta = Math.pow(delta / 100, exp_level) * 100;
var weight = 100 - delta * (100 - 54) / total_fade_level / 100;
node.style.setProperty("background-color", 'hsl(151, 42%, '+weight+'%)', "important"); /* min 54% or 77 */
}
else if(delta < 50){
delta = 100 - Math.pow((100 - delta) / 100, exp_level) * 100;
var weight = 100 - (100 - delta) * (100 - 68) / total_fade_level / 100;
node.style.setProperty("background-color", 'hsl(5, 70%, '+weight+'%)', "important"); /* min 68% or 84 */
}
}
}
}
},
init: function(options) {
var table, tables, _i, _len, _results;
if (options == null) {
options = {};
}
if (options.selector == null) {
options.selector = 'table';
}
tables = document.querySelectorAll(options.selector);
_results = [];
for (_i = 0, _len = tables.length; _i < _len; _i++) {
table = tables[_i];
/* must have 1 header */
if (table.tHead && table.tHead.rows.length == 1) {
table.classList.add('sortable');
}
else{
console.log("Table can not be sorted: no headers", table);
continue;
}
_results.push(sortable.initTable(table));
}
return _results;
},
initTable: function(table) {
var i, th, ths, _i, _len, _ref;
if (((_ref = table.tHead) != null ? _ref.rows.length : void 0) !== 1) {
return;
}
if (table.getAttribute('data-sortable-initialized') === 'true') {
return;
}
table.setAttribute('data-sortable-initialized', 'true');
ths = table.querySelectorAll('thead th');
for (i = _i = 0, _len = ths.length; _i < _len; i = ++_i) {
th = ths[i];
if (th.getAttribute('data-sortable') !== 'false') {
sortable.setupClickableTH(table, th, i);
}
}
thft = table.querySelectorAll('tfoot th');
for (i = _i = 0, _len = thft.length; _i < _len; i = ++_i) {
th = thft[i];
if (th.getAttribute('data-sortable') !== 'false') {
sortable.setupClickableTH(table, th, i);
}
}
return table;
},
setupClickableTH: function(table, th, i) {
var eventName, onClick, type, _i, _len, _results;
type = sortable.getColumnType(table, i);
onClick = function(e) {
var compare, item, newSortedDirection, position, row, rowArray, sorted, sortedDirection, tBody, ths,
value, _compare, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1;
if (e.handled !== true) {
e.handled = true;
} else {
return false;
}
sorted = this.getAttribute('data-sorted') === 'true';
sortedDirection = this.getAttribute('data-sorted-direction');
if (sorted) {
newSortedDirection = sortedDirection === 'ascending' ? 'descending' : 'ascending';
} else {
newSortedDirection = type.defaultSortDirection;
}
ths = this.parentNode.querySelectorAll('th');
for (_i = 0, _len = ths.length; _i < _len; _i++) {
th = ths[_i];
th.setAttribute('data-sorted', 'false');
th.removeAttribute('data-sorted-direction');
}
this.setAttribute('data-sorted', 'true');
this.setAttribute('data-sorted-direction', newSortedDirection);
tBody = table.tBodies[0];
rowArray = [];
if (!sorted) {
if (type.compare != null) {
_compare = type.compare;
} else {
_compare = function(a, b) {
return b - a;
};
}
compare = function(a, b) {
if (a[0] === b[0]) {
return a[2] - b[2];
}
if (type.reverse) {
return _compare(b[0], a[0]);
} else {
return _compare(a[0], b[0]);
}
};
_ref = tBody.rows;
for (position = _j = 0, _len1 = _ref.length; _j < _len1; position = ++_j) {
row = _ref[position];
value = sortable.getNodeValue(row.cells[i]);
if (type.comparator != null) {
value = type.comparator(value);
}
rowArray.push([value, row, position]);
}
rowArray.sort(compare);
for (_k = 0, _len2 = rowArray.length; _k < _len2; _k++) {
row = rowArray[_k];
tBody.appendChild(row[1]);
}
} else {
_ref1 = tBody.rows;
for (_l = 0, _len3 = _ref1.length; _l < _len3; _l++) {
item = _ref1[_l];
rowArray.push(item);
}
rowArray.reverse();
for (_m = 0, _len4 = rowArray.length; _m < _len4; _m++) {
row = rowArray[_m];
tBody.appendChild(row);
}
}
if (typeof window['CustomEvent'] === 'function') {
return typeof table.dispatchEvent === "function"
? table.dispatchEvent(new CustomEvent('Sortable.sorted', {
bubbles: true
})) : void 0;
}
};
_results = [];
for (_i = 0, _len = clickEvents.length; _i < _len; _i++) {
eventName = clickEvents[_i];
_results.push(addEventListener(th, eventName, onClick));
}
return _results;
},
getColumnType: function(table, i) {
var row, specified, text, type, _i, _j, _len, _len1, _ref, _ref1, _ref2;
specified = (_ref = table.querySelectorAll('th')[i]) != null ? _ref.getAttribute('data-sortable-type')
: void 0;
if (specified != null) {
return sortable.typesObject[specified];
}
_ref1 = table.tBodies[0].rows;
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
row = _ref1[_i];
text = sortable.getNodeValue(row.cells[i]);
_ref2 = sortable.types;
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
type = _ref2[_j];
if (type.match(text)) {
return type;
}
}
}
return sortable.typesObject.alpha;
},
getNodeValue: function(node) {
var dataValue;
if (!node) {
return '';
}
dataValue = node.getAttribute('data-value');
if (dataValue !== null) {
return dataValue;
}
if(typeof node.children[0] != "undefined" && typeof node.children[0].value != "undefined"
&& node.children[0].value){
return ""+node.children[0].value;
}
if (typeof node.innerText !== 'undefined') {
return node.innerText.replace(trimRegExp, '');
}
return node.textContent.replace(trimRegExp, '');
},
setupTypes: function(types) {
var type, _i, _len, _results;
sortable.types = types;
sortable.typesObject = {};
_results = [];
for (_i = 0, _len = types.length; _i < _len; _i++) {
type = types[_i];
_results.push(sortable.typesObject[type.name] = type);
}
return _results;
}
};
sortable.setupTypes([
{
name: 'numeric',
defaultSortDirection: 'ascending',
reverse: true,
prepare: function(a) {
a = a.toString();
a = a.replace(/\u20ac/g, ''); /*euro*/
a = a.replace(/\u0024/g, ''); /*dollar*/
a = a.replace(/,/g, '');
a = a.replace(/ /g, '');
if(a.toLowerCase().indexOf('k') > -1 && a.replace(/k$/ig, '').match(numberRegExp)){
a = (parseFloat(a.replace(/k$/ig, '')) * 1000).toString();
}
else if(a.toLowerCase().indexOf('m') > -1 && a.replace(/m$/ig, '').match(numberRegExp)){
a = (parseFloat(a.replace(/m$/ig, '')) * 1000 * 1000).toString();
}
if(a == '-' || a.toLowerCase() == 'n/a') a = '0';
return a;
},
match: function(a) {
a = this.prepare(a);
return a.match(numberRegExp);
},
comparator: function(a) {
a = this.prepare(a);
return parseFloat(a.replace(/[^0-9.-]/g, ''), 10) || 0;
}
},
{
name: 'date',
defaultSortDirection: 'ascending',
reverse: true,
prepare: function(a) {
if(a == '-' || a.toLowerCase() == 'n/a') a = '0000';
return a;
},
match: function(a) {
a = this.prepare(a);
return !isNaN(Date.parse(a));
},
comparator: function(a) {
a = this.prepare(a);
return Date.parse(a) || 0;
}
},
{
name: 'time',
defaultSortDirection: 'ascending',
reverse: true,
prepare: function(a) {
a = a.replace(/:/g, '.'); /*change time to float*/
if(a == '-' || a.toLowerCase() == 'n/a') a = '0';
return a;
},
match: function(a) {
a = this.prepare(a);
return a.match(numberRegExp);
},
comparator: function(a) {
a = this.prepare(a);
return parseFloat(a.replace(/[^0-9.-]/g, ''), 10) || 0;
}
},
{
name: 'alpha', /* make sure it is last */
defaultSortDirection: 'ascending',
reverse: false,
prepare: function(a) {
return a;
},
match: function() {
return true;
},
compare: function(a, b) {
return a.localeCompare(b);
}
}
]);
setTimeout(function(){
sortable.prepare();
sortable.init();
sortable.highlight();
}, 0);
if (typeof define === 'function' && define.amd) {
define(function() {
return sortable;
});
} else if (typeof exports !== 'undefined') {
module.exports = sortable;
} else {
window.Sortable = sortable;
}
})();
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |