JSFiddle - React, Tailwind, and code Playground

by Mawel Don

HTML

Click a Column Header
<table>
    <tbody>
        <tr class="ignore">
            <th>Shop Name</th>
            <th>Note Gloable</th>
            <th>User 0</th>
            <th>User 1</th>
            <th>User 2</th>
            <th>User 3</th>
        </tr>

        <tr>
            <td>Shop 0</td>
            <td>3.5714285714285716</td>
            <td>25</td>
            <td>13</td>
            <td>12</td>
            <td>32</td>
        </tr>
        <tr>
            <td>Shop 1</td>
            <td>2</td>
            <td>18.666666666666664</td>
            <td>8.333333333333332</td>
            <td>5</td>
            <td>23.333333333333332</td>
        </tr>
        <tr>
            <td>Shop 3</td>
            <td>3.142857142857143</td>
            <td>22</td>
            <td>13</td>
            <td>11</td>
            <td>26</td>
        </tr>
        <tr>
            <td>Shop 4</td>
            <td>2.6</td>
            <td>21.666666666666664</td>
            <td>14.333333333333332</td>
            <td>12.333333333333332</td>
            <td>16.333333333333332</td>
        </tr>
        <tr>
            <td>Shop 5</td>
            <td>4.333333333333333</td>
            <td>30.333333333333332</td>
            <td>10</td>
            <td>10</td>
            <td>30.333333333333332</td>
        </tr>
        <tr>
            <td>Shop 2</td>
            <td>7.5</td>
            <td>36.666666666666664</td>
            <td>17.333333333333332</td>
            <td>14.666666666666666</td>
            <td>50.666666666666664</td>
        </tr>
    </tbody>
</table>

CSS

table {
    border-collapse: collapse;
}

table th {
    cursor: pointer;
}

table tr,
table td {
    padding: 3px;
    border-style: dotted;
    border: 1px solid #000;
}

JavaScript

[...document.querySelectorAll('th')].forEach(th => {
    th.addEventListener('click', () => {
        const table = getParentUntil(th, 'table');
        let rows = [...table.querySelectorAll('tr:not(.ignore)')]
            .sort(sorter([...th.parentElement.children].indexOf(th)));
        th.dataset.asc = !(JSON.parse((th.dataset.asc || "true").toLowerCase()));
        if (th.dataset.asc === "true") {
            rows = rows.reverse();
        }
        rows.forEach(row => table.appendChild(row));
    });
});

function getParentUntil(el, tag) {
    while ((el = el.parentElement) && !el.matches(tag));
    return el;
}

function sorter(index) {
    return (a, b) => {
        const aVal = cellValue(a, index);
        const bVal = cellValue(b, index);
        return isNumber(aVal) && isNumber(bVal) ? aVal - bVal : aVal.localeCompare(bVal);
    };
}

function cellValue(row, index) {
    return [...row.children][index].innerText;
}

function isNumber(n) {
	const number = parseFloat(n);
    return !Number.isNaN(number) && Number.isFinite(number);
}