1
\$\begingroup\$

I've a table in HTML looks like this:

Subjects n1 n2 n3
subject1 10 0 0
subject2 0 5 20
<table>
   <thead>
      <tr>
         <th class="subject">Subjects</th>
         <th>n1</th>
         <th>n2</th>
         <th>n3</th>
      </tr>
   </thead>
   <tbody>
      <tr>
         <th class="subject">subject1</th>
         <td>10</td>
         <td>0</td>
         <td>0</td>
      </tr>
      <tr>
         <th class="subject">subject2</th>
         <td>0</td>
         <td>5</td>
         <td>20</td>
      </tr>
   </tbody>
</table>

Is there any thought or approach with javascript I could re-order columns in a specific order let order = ['n2','n1','n3']:

Subjects n2 n1 n3
subject1 0 10 0
subject2 5 0 20

I've solved by turning the table into 2-dimensional array and sort it and turn it back into table HTML:

function tableToArray(tbl, opt_cellValueGetter) {
  opt_cellValueGetter = opt_cellValueGetter || function(td) {
    return td.textContent || td.innerText;
  };
  var twoD = [];
  for (var rowCount = tbl.rows.length, rowIndex = 0; rowIndex < rowCount; rowIndex++) {
    twoD.push([]);
  }
  for (var rowIndex = 0, tr; rowIndex < rowCount; rowIndex++) {
    var tr = tbl.rows[rowIndex];
    for (var colIndex = 0, colCount = tr.cells.length, offset = 0; colIndex < colCount; colIndex++) {
      var td = tr.cells[colIndex],
        text = opt_cellValueGetter(td, colIndex, rowIndex, tbl);
      while (twoD[rowIndex].hasOwnProperty(colIndex + offset)) {
        offset++;
      }
      for (var i = 0, colSpan = parseInt(td.colSpan, 10) || 1; i < colSpan; i++) {
        for (var j = 0, rowSpan = parseInt(td.rowSpan, 10) || 1; j < rowSpan; j++) {
          twoD[rowIndex + j][colIndex + offset + i] = text;
        }
      }
    }
  }
  return twoD;
}
let order = ['n2', 'n1', 'n3', "Subjects"];
const sort2dArrayColumsByFirstRow = (array) => {
  if (!Array.isArray(array)) return [];
  const sortedFirstRow = array[0]
    .map((item, i) => ({
      v: item,
      i: i
    }))
    .sort((a, b) => {
      return order.indexOf(a.v) - order.indexOf(b.v);
    });
  return array.map((row) => row.map((_, i) => row[sortedFirstRow[i].i]));
};

function arrayToTable(columnNames, dataArray) {
  var myTable = document.createElement('table');
  var y = document.createElement('tr');
  myTable.appendChild(y);

  for (var i = 0; i < columnNames.length; i++) {
    var th = document.createElement('th'),
      columns = document.createTextNode(columnNames[i]);
    th.appendChild(columns);
    y.appendChild(th);
  }

  for (var i = 0; i < dataArray.length; i++) {
    var row = dataArray[i];
    var y2 = document.createElement('tr');
    for (var j = 0; j < row.length; j++) {
      myTable.appendChild(y2);
      var th2 = document.createElement('td');
      var date2 = document.createTextNode(row[j]);
      th2.appendChild(date2);
      y2.appendChild(th2);
    }
  }
  document.querySelector('#tableEl').innerHTML = myTable.innerHTML;
}
let arr = tableToArray(document.querySelector('#tableEl'))
let arrOrdered = sort2dArrayColumsByFirstRow(arr);
arrayToTable(arrOrdered[0], arrOrdered.slice(1))
table {
  border-collapse: collapse;
  border: 1px solid;
}
tr,
th,
td {
  border: 1px solid;
  padding: 3px 10px;
}
<table id="tableEl">
  <thead>
    <tr>
      <th class="subject">Subjects</th>
      <th>n1</th>
      <th>n2</th>
      <th>n3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th class="subject">subject1</th>
      <td>10</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr>
      <th class="subject">subject2</th>
      <td>0</td>
      <td>5</td>
      <td>20</td>
    </tr>
  </tbody>
</table>

Is it the right way of doing it?

\$\endgroup\$
1

1 Answer 1

1
\$\begingroup\$

Start with the data in Javascript. Always sort the Javascript, never from the html table. Always create a new table. Throwing away the pre-existing table fantastically simplifies everything.

I strongly recommend creating a JS object that holds the data, sorts, and a method outputting the "flat" data row. The object is for manipulating and traversing the data structure as needed. HTML is just display. Keep all the necessary element cross-referencing within this object. Give the html "flattened" data and table creation is dead simple.

"Rebuild every time" will be more than fast enough. It will not keep the user waiting.

\$\endgroup\$
1
  • \$\begingroup\$ Agreed. Even building an HTML string and setting the innerHTML on an element will be faster than the myriad redraw events that would happen by detaching an element and then repositioning it inside the table. Create a DOM fragment, set the innerHTML, remove the old table and insert the DOM fragment in place of the old table. \$\endgroup\$ Sep 21 at 18:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.