Allan Doe
Allan Doe

Reputation: 11

How to sort an HTML table?

I am not at all an HTML expert. I program microcontrollers and got off onto a tangent.

I created an html doc to show a table of microcontroller registers, register addresses and register descriptions. I created a table with 3 columns - and about 120 rows. Some of the register addresses are bit addressable - if their addresses end in 0 or 8.

I wanted to highlight these 'special' register addresses - by showing them in red. So, in the table cells with a register address ending in 0 or 8, I use "" and "" to surround the address value.

My table has 3 columns, Register, Address, and Description. Then one row might look like

"ACC 0xE0 Accumulator".

I got my table all done and it looks great. Then I got the idea that I want to be able to sort the table on any column. For example, if I click on "Address" I want the table to re-display and sort by the values in that column.

I searched, and found a way to do it. It works by having a "sort" button - click on that and it re-displays sort on the first column values. I implemented a simple version of it and got it working. I then changed it to instead sort on the second column when the "sort" button was clicked.

That didn't exactly work .... because of all those "" whatevers.

The example I copied from his here:

https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_sort_table

Is there anything 'simple' I can do to use this method - but still be able have certain entries in the Address column be shown red ?

I should probably stick to programming microcontrollers, but I like a challenge.

Upvotes: 1

Views: 8338

Answers (2)

Mister Jojo
Mister Jojo

Reputation: 22320

sample code for sort on a selected column,
that's free...

const
  myButtonSort  = document.querySelector('#my-button-sort')
, colSelector   = document.querySelector('#sel-col')
, myTable_tBody = document.querySelector('#my-table > tbody')
  ;
myButtonSort.onclick = _ =>
  {
  let col = parseInt(colSelector.value ); 
  [...myTable_tBody.querySelectorAll('tr')]
    .map(row=>({row, str:row.cells[col].textContent }))
    .sort((a,b)=>a.str.localeCompare(b.str))
    .forEach(el=>
      {
      myTable_tBody.appendChild(el.row)
      })
  }
table  {
  border-collapse : collapse;
  margin          : 2em 1em;
  }
td,th  {
  padding    : .2em .8em;
  border     : 1px solid darkblue;
  }
thead {
  background : lightseagreen ;
  }
<select id="sel-col">
  <option value="0">column x</option>
  <option value="1">column y</option>
</select>

<button id="my-button-sort">sort</button>

<table id="my-table">
  <thead>
    <tr> <th>x</th> <th>y</th> </tr>
  </thead>
  <tbody>
    <tr> <td>aa</td><td> 1 </td></tr>
    <tr> <td>zz</td><td> 2 </td></tr>
    <tr> <td>ee</td><td> 3 </td></tr>
    <tr> <td>cc</td><td> 4 </td></tr>
  </tbody>
</table>

Ascending and descending sort example:

const myArray = 
  [ { worth: '100',  name: 'jessca', reason: 'money', email: '[email protected]',  number: '4456',  instagram: 'hvg_ujh', tiktok: 'hhgh.thg' } 
  , { worth: '265',  name: 'e',      reason: 'money', email: '[email protected]',  number: '3456',  instagram: 'hvg_ujh', tiktok: 'hhgh.thg' } 
  , { worth: '6000', name: 'ssica',  reason: 'sex',   email: '[email protected]',  number: '0456',  instagram: 'hvg_ujh', tiktok: 'hhgh.thg' } 
  , { worth: '855',  name: 'sica',   reason: 'sex',   email: '[email protected]', number: '9456',  instagram: 'hvg_ujh', tiktok: 'hhgh.thg' } 
  , { worth: '8679', name: 'ica',    reason: 'sex',   email: '[email protected]',  number: '0756',  instagram: 'hvg_ujh', tiktok: 'hhgh.thg' } 
  , { worth: '1',    name: 'ca',     reason: 'money', email: '[email protected]',  number: '14856', instagram: 'hvg_ujh', tiktok: 'hhgh.thg' } 
  ] 
const
  t_Head      = document.querySelector('#myTable thead')
, t_Head_THs  = document.querySelectorAll('#myTable thead tr th')
, th_list     = [...t_Head_THs].map( TH => TH.dataset.column)
, t_Body      = document.querySelector('#myTable tbody')
, sortOrder   = [ 'none' ,'asc', 'desc' ]
, sortType    = { worth: 'num', name:'str', reason:'str', email:'str', number:'num', instagram:'str', tiktok:'str' }
, sortProcess =
  { 'asc:num'  : (a,b) => +a.str - +b.str
  , 'desc:num' : (a,b) => +b.str - +a.str
  , 'asc:str'  : (a,b) => a.str.localeCompare(b.str)
  , 'desc:str' : (a,b) => b.str.localeCompare(a.str)
  };
myArray.forEach( row =>
  {
  let TR = t_Body.insertRow()
  for (col of th_list)
    TR.insertCell().textContent = row[col] 
  })
t_Head.onclick = ({target}) =>
  {
  if (!target.matches('th')) return
  
  let
    dataOrder = sortOrder[(sortOrder.indexOf(target.dataset.order) +1 )% sortOrder.length]
  , dataType  = sortType[target.dataset.column]
    ;
  t_Head_THs.forEach( TH => { TH.dataset.order = (TH===target) ? dataOrder : 'none' })

  if (dataOrder !== 'none')
    {
    [...t_Body.querySelectorAll('tr')]
    .map     ( row => ({row, str:row.cells[target.cellIndex].textContent }))
    .sort    ( sortProcess[`${dataOrder}:${dataType}`])
    .forEach ( elm => t_Body.appendChild(elm.row ))
    }
  }
body { 
  font-family : Arial, Helvetica, sans-serif;
  font-size   : 16px;
  }
table {
  border-collapse  : separate;
  border-spacing   : 1px;
  background-color : darkblue;
  margin           : 1em; 
  }
th, td {
  border     : none;
  background : whitesmoke;
  padding    : .3em .4em;
  }
th {
  background-color : #76ced1;
  white-space      : nowrap;
  cursor           : pointer;
  }
th::before {
  content        : attr(data-column) ' ';
  text-transform : capitalize;
  }
th[data-order=asc]::after  { content : '\25B2'; }
th[data-order=desc]::after { content : '\25BC'; }
th[data-order=none]::after { content : '\25B2'; color:transparent; }  /* get the same width */
<table  id="myTable">
  <thead>
    <tr>
      <th data-column="worth"     data-order="none"></th>
      <th data-column="name"      data-order="none"></th>
      <th data-column="reason"    data-order="none"></th>
      <th data-column="email"     data-order="none"></th>
      <th data-column="number"    data-order="none"></th>
      <th data-column="instagram" data-order="none"></th>
      <th data-column="tiktok"    data-order="none"></th>
    </tr>
  </thead>
  <tbody></tbody>
</table>

Upvotes: 3

vanowm
vanowm

Reputation: 10201

This might get you started:

const table = document.getElementById("test"),
      th = test.querySelectorAll("th"),
      sortDefault = 0, //default sorted column number
      orderDefault = 0; //default order: 0 = ascending, 1 = descending

table.dataset.sort = sortDefault;
table.dataset.order = orderDefault;

/* add click listeners on table headers */
for(let i = 0; i < th.length; i++)
{
  th[i].addEventListener("click", e =>
  {
    /* if this column was sorted, change it's order */
    if (+table.dataset.sort == e.target.cellIndex)
      table.dataset.order = +table.dataset.order ? 0 : 1;

    /* tell table which column is currently sorted */
    table.dataset.sort = e.target.cellIndex;

    sortColumn();
  });
}

populateTable(); //fill table with random data
sortColumn(); //initial sort of the table

function sortColumn()
{
  const rows = Array.from(table.children);
  rows.splice(0, 1); //remove header from the list
  rows.sort((a, b) => 
  {
    a = a.children[table.dataset.sort].textContent;
    b = b.children[table.dataset.sort].textContent;

    a = a.replace(/\W/g, ""); //remove non alphanumerical characters
    b = b.replace(/\W/g, ""); //remove non alphanumerical characters

    a = a.replace(/0x[a-fA-F0-9]+/, n => Number(n)); //convert 0xHEX to decimal
    b = b.replace(/0x[a-fA-F0-9]+/, n => Number(n)); //convert 0xHEX to decimal

    return a.localeCompare(b, undefined, {numeric: true, sensitivity: 'base'})
  });

  if (+table.dataset.order)
    rows.reverse();

  for(let i = 0; i < rows.length; i++)
  {
    table.appendChild(rows[i]);
  }
}


function populateTable()
{
  for(let i = 0, _tr = document.createElement("tr"), _td = document.createElement("td"); i < 10; i++)
  {
    const tr = _tr.cloneNode(true);
    let td = _td.cloneNode(true);
    td.textContent = "reg " + rand(0, 199);
    tr.appendChild(td);

    td = _td.cloneNode(true);
    td.textContent = "address 0x" + rand(0, 255).toString(16);
    if (rand(0,1))
      td.textContent = td.textContent.replace(/ (.*)/, ' "$1"');
    tr.appendChild(td);

    td = _td.cloneNode(true);
    td.textContent = "desc " + rand(0, 99999).toString(16);
    tr.appendChild(td);
    table.appendChild(tr);
  }
  function rand(min, max)
  {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
}
#test
{
  border-collapse: collapse;
}
#test tr td
{
  border: 1px solid black;
}
#test td
{
  padding: 0.5em 1em;
}
#test th
{
  user-select: none;
  cursor: pointer;
}
#test th:after
{
  visibility: hidden;
}
#test[data-order="0"] th:after
{
  content: "▲";
}
#test[data-order="1"] th:after
{
  content: "▼";
}
#test[data-sort="0"] th:nth-child(1):after,
#test[data-sort="1"] th:nth-child(2):after,
#test[data-sort="2"] th:nth-child(3):after
{
  visibility: visible;
}
<table id="test">
  <tr>
    <th>Register</th>
    <th>Address</th>
    <th>Description</th>
  </tr>
</table>

Upvotes: 1

Related Questions