Sortable Table with One‑Click Column Sorting
This table allows you to sort column values in ascending or descending order simply by clicking the header row.
It is built with lightweight JavaScript and CSS, and can be added to your project with a simple copy and paste.
Smooth and efficient, it’s ideal for business tools and data list displays.
| Name | Age | Position | Income |
|---|---|---|---|
| Ishii | 37 | Director | 90,000 |
| Yamada | 34 | Staff | 90,000 |
| Kawasaki | 29 | Producer | 70,000 |
| Nishida | 29 | Staff | 70,000 |
| Sato | 18 | Director | 50,000 |
| Ishikawa | 21 | Staff | 53,000 |
Source Code
This is the CSS, JavaScript, and HTML you can use simply by copying and pasting.
<style>
table.sortableTable{
border-collapse:collapse;
padding:0;
}
table.sortableTable>thead>tr>th{
cursor:pointer;
border-top:2px solid #EED;
border-bottom:3px solid #CCB;
border-right:1px solid #AA9;
border-left:1px solid #FFF;
background-color: #EED;
margin:0;
padding:0.1em 1.2em 0.1em 0.1em;
position:relative;
}
table.sortableTable>thead>tr>th::after{
position:absolute;
right:0;
top:0;
bottom:0;
content:"";
}
table.sortableTable>thead>tr>th.up::after{
position:absolute;
right:0;
top:0;
bottom:0;
content:"▼";
}
table.sortableTable>thead>tr>th.down::after{
position:absolute;
right:0;
top:0;
bottom:0;
content:"▲";
}
table.sortableTable>thead>tr>th:hover{
border-top:2px solid #FFC83C;
}
table.sortableTable>tbody{
background-color:#FFF;
}
table.sortableTable>tbody>tr>td{
margin:0px;
padding:2px;
border:1px solid #EEE; /* Bottom border for each row */
}
</style>
<script>
class TMamSortableTable{
// Constructor
constructor(){
this.elms=[];
let el=document.querySelectorAll(".sortableTable");
let ct=0;
for(let i=0;i<el.length;i++){
if(el[i].tHead){
this.elms[ct]=[];
this.elms[ct].elm=el[i];
this.elms[ct].col=-1;
this.elms[ct].sort=0;
this.elms[ct].rows=[];
// Add click events to header cells
for(let j=0;j<this.elms[ct].elm.tHead.children[0].children.length;j++){
this.elms[ct].elm.tHead.children[0].children[j].addEventListener(
"click", this.sortTable.bind(this,ct,j)
);
}
// Store all body rows
for(let j=0;j<this.elms[ct].elm.tBodies[0].children.length;j++){
this.elms[ct].rows[j]=this.elms[ct].elm.tBodies[0].children[j];
}
ct++;
}
}
}
// Sort the table and reinsert rows
sortTable(tnum,colnum){
this.remove_rows(tnum);
let arr=[];
let clr=this.elms[tnum].rows;
for(let i=0;i<clr.length;i++){
arr.push({"idx":i,val:clr[i].children[colnum].innerText});
}
// Determine sort direction
if(this.elms[tnum].col!==colnum){
this.elms[tnum].col=colnum;
this.elms[tnum].sort=1;
}else{
this.elms[tnum].sort=-this.elms[tnum].sort;
}
// Ascending
if(this.elms[tnum].sort===1){
arr.sort(function(a,b){
if(a.val>b.val){ return 1; }
else if(a.val<b.val){ return -1; }
else{ return 0; }
});
}
// Descending
else{
arr.sort(function(a,b){
if(a.val>b.val){ return -1; }
else if(a.val<b.val){ return 1; }
else{ return 0; }
});
}
// Reinsert rows in sorted order
for(let i=0;i<arr.length;i++){
this.elms[tnum].elm.tBodies[0].appendChild(this.elms[tnum].rows[arr[i].idx]);
}
// Update header sort indicators
let hd=this.elms[tnum].elm.tHead.children[0];
for(let i=0;i<hd.children.length;i++){
hd.children[i].classList.remove("down");
hd.children[i].classList.remove("up");
}
if(this.elms[tnum].sort===1){
hd.children[colnum].classList.add("up");
}else{
hd.children[colnum].classList.add("down");
}
}
// Remove all rows
remove_rows(tnum){
while(this.elms[tnum].elm.tBodies[0].firstElementChild){
this.elms[tnum].elm.tBodies[0].removeChild(
this.elms[tnum].elm.tBodies[0].firstElementChild
);
}
}
}
let MamSortableTable;
window.addEventListener("DOMContentLoaded",function(){
MamSortableTable=new TMamSortableTable;
});
</script>
