Makes any table with class="sortable", er, sortable. The user can click on a table header and change the sorting of the table rows.
Just include the JavaScript and it will work. No function calls needed, all is done with an eventListener. (the CSS is not strictly needed, but makes it pretty-ish and user friendly)
You can find a simple demo on https://tofsjonas.github.io/sortable/
- Factoids
- "Installation"
- Non-sortable field
- Indicators/arrows on the left side
- Note about css/scss
- Sorting dates, sizes and such
- Alternative sorting
- Colspans/Sort on specific column
- Ascending sort
- Tiebreaker / secondary sort
- Sort on load
- ...with a little help from my friends
-
1006 bytes minified. Back under 1k! 🥳 (550 bytes gzipped)
-
Works with JavaScript generated tables. (since we are using an eventListener)
-
Lightning fast. Huge tables will make it slow and may freeze the browser, especially for mobiles, so you know...
-
Requires thead and tbody.
-
cross browser,
ie9+No longer ie9 compatible. Then again, maybe it already wasn't 🤷 -
eventListeners attached to the rows WILL be removed -
eventListeners are no longer removed! 😊
-
NOT tested with React, Angular, Vue, etc.
-
Works with Svelte!
There are three ways to use sortable, all of which have their pros and cons. S Anand and dkhgh had some interesting thoughts about it.
-
Including a link to jsDelivr. (easiest)
-
Copy the file from jsDelivr or Github and put it in your assets folder. (in between)
-
Installing the npm package. (most work)
<table class="sortable">
<thead>
<tr>
<th><span>Role</span></th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>Genius</td>
<td>Rick</td>
</tr>
<tr>
<td><a href="javascript:alert('Inline javascript works!');">Sidekick</a></td>
<td>Morty</td>
</tr>
</tbody>
</table>
<link href="https://cdn.jsdelivr.net/gh/tofsjonas/sortable@latest/sortable.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/gh/tofsjonas/sortable@latest/sortable.min.js"></script>
(The span
on line four is just there to prove that elements inside th
works)
Same as above, but link to your own files
...
<link href="/assets/sortable.min.css" rel="stylesheet" />
<script src="/assets/sortable.min.js"></script>
...
First,
npm install sortable-tablesort
# yarn add sortable-tablesort
# pnpm install sortable-tablesort
Now you can
Same as above, with files
...
<link href="./node_modules/sortable-tablesort/sortable.min.css" rel="stylesheet" />
<script src="./node_modules/sortable-tablesort/sortable.min.js"></script>
...
or
// main.js
import 'sortable-tablesort/sortable.min.css'
import 'sortable-tablesort/sortable.min.js'
If you wish to disable sorting for a specific field, the easiest way is to add a class to it, like so:
<tr>
<th class="no-sort">Role</th>
<th>Name</th>
</tr>
and then use css to block clicks. like so:
.sortable th.no-sort {
pointer-events: none;
}
This is a bit trickier, but it doesn't require any changes to the html, so I guess it could be worth it in some cases.
/* the first column in every sortable table should not be sortable*/
.sortable th:nth-child(1) {
pointer-events: none;
}
/* the seventh column in the second .sortable table should not be sortable*/
.sortable:nth-of-type(2) th:nth-child(7) {
pointer-events: none;
}
If you have text that is aligned on the right side, you may want to have the arrows on the left side.
This is solved by adding a class to the css and using ::before
instead of ::after
.
(You can of course use a pure css solution, without class names - just like with the non-sortable field - but that I will leave for you to figure out.)
.sortable th.indicator-left::after {
content: '';
}
.sortable th.indicator-left::before {
margin-right: 3px;
content: '▸';
}
/* etc. */
The css/scss
in this repo was only ever meant as an example. It was never intended to be actually used.
That said, if you're feeling lazy, here are two stylesheets you can use:
<!-- This will add arrows, as well as support for .no-sort and .indicator-left -->
<link href="https://cdn.jsdelivr.net/gh/tofsjonas/sortable@latest/sortable-base.min.css" rel="stylesheet" />
<!-- This will make it look like the tables in the example, with arrows, striped rows etc. -->
<link href="https://cdn.jsdelivr.net/gh/tofsjonas/sortable@latest/sortable.min.css" rel="stylesheet" />
Using the data-sort
attribute in tbody
> td
you can have one visible value and one sortable value. This is useful in case you have for instance sizes like kb, Mb, GB, or just really weird dates/other (feet, inches, stone, yards, miles, etc.). 😉
<table class="sortable">
<thead>
<tr>
<th>Movie Name</th>
<th>Size</th>
<th>Release date</th>
</tr>
</thead>
<tbody>
<tr>
<td>Zack Snyder's Justice League</td>
<td data-sort="943718400">900MB</td>
<td data-sort="20210318">03/18/2021</td>
</tr>
<tr>
<td>The Sound of Music</td>
<td data-sort="1610612736">1.5GB</td>
<td data-sort="19651209">12/09/1965</td>
</tr>
</tbody>
</table>
If you click on a table header while holding shift or alt an alternative
data-sort-alt
attribute will override data-sort
.
<table class="sortable">
<thead>
<tr>
<th>Movie Name</th>
<th>Size</th>
<th>Release date</th>
</tr>
</thead>
<tbody>
<tr>
<td>Something</td>
<td data-sort-alt="c" data-sort="a">A</td>
<td data-sort-alt="b" data-sort="c">B</td>
<td data-sort-alt="a" data-sort="b">C</td>
</tr>
<tr>
<td>Something else</td>
<td data-sort-alt="e" data-sort="f">D</td>
<td data-sort-alt="f" data-sort="e">E</td>
<td data-sort-alt="d" data-sort="d">F</td>
</tr>
</tbody>
</table>
Using the data-sort-col
attribute in thead
> th
, you can sort on a different column than the one that was clicked. For instance if you want to have colspans. Like so:
<thead>
<tr>
<th></th>
<th>Category</th>
<th class="show_name">Show</th>
<th colspan="2">Overall</th>
<th colspan="2" data-sort-col="5">On Our Dates</th>
<th data-sort-col="7">First Sold Out</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tags"> </td>
<td class="category">Comedy</td>
<td class="show_name">Show 1</td>
<td class="ratio all" data-sort="72">18/25</td>
<td class="pct all">72%</td>
<td class="ratio ours" data-sort="75">3/4</td>
<td class="pct ours">75%</td>
<td>2022-07-30</td>
</tr>
...
</tbody>
By adding asc
to table
, the default sorting direction will be ascending instead of descending
<table class="sortable asc">
<thead>
...
</thead>
<tbody>
...
</tbody>
</table>
If you wish to sort by a different column when two values are equal, you can use the data-sort-tbr
attribute, like so:
<table class="sortable asc">
<thead>
<tr>
<th data-sort-tbr="1">Year</th>
<th>Month</th>
<th>Day</th>
</tr>
</thead>
<tbody>
<tr>
<td>2010</td>
<td>07</td>
<td>25</td>
</tr>
<tr>
<td>2010</td>
<td>11</td>
<td>12</td>
</tr>
<tr>
<td>2010</td>
<td>04</td>
<td>25</td>
</tr>
</tbody>
</table>
When clicking Year, if they are the same, we will sort on month.
If you wish to sort a table on load, I would recommend doing something like this:
<table class="sortable">
<thead>
<tr>
<th>Movie Name</th>
<th id="movie-size">Size</th>
<th>Release date</th>
</tr>
</thead>
<tbody>
...
</tbody>
</table>
<script>
window.addEventListener('load', function () {
const el = document.getElementById('movie-size')
// without id:
// const el = document.querySelector('.sortable th:first-child')
// const el = document.querySelector('.sortable th:nth-child(2)')
// const el = document.querySelectorAll('.sortable')[3].querySelector('th:nth-child(7)')
// etc.
if (el) {
el.click()
}
})
</script>
Combine this with <table class="sortable asc">
to reverse the sort order. Or do el.click()
twice!
-
<table class="sortable asc">
let's you sort ascending by default. Thanks Nikita Dunajevs! -
data-sort-alt
intbody
>td
allows for alternative sorting while holdingshift
oralt
. Thanks wodny! -
data-sort-col
inthead
>th
allows you to specify which column should be sorted, in case you are usingcolspan
, for instance. Thanks Nick Kocharhook! -
Nested elements inside
th
now works. Thanks mxve! -
Sort on load example. Thanks Christian Petersson and Abit Salihu!
-
If you have more than one
<tbody />
, they will all be sorted. (Multiple<thead />
s are not "allowed".) Thanks GazHay! -
Thanks to Gordan Ratkovic for the Tiebreaker / secondary sort idea!
-
Thanks to chatcoda for the
<td></td>
/<td>0</td>
sorting bug fix! -
Thanks to Christian Garbs for fixing the
dataset
bug! -
Thanks to Witold Baryluk for pointing out some inefficiencies, bringing it back under 1k in size!