Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<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 - &euro;</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>&euro;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>&euro;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>&euro;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>&euro;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, ''); /* &nbsp; */
                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

Dismiss x
public
Bin info
anonymouspro
0viewers