Home > Net >  Re-order table columns in HTML dynamically with Javascript
Re-order table columns in HTML dynamically with Javascript

Time:09-21

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 >Subjects</th>
         <th>n1</th>
         <th>n2</th>
         <th>n3</th>
      </tr>
   </thead>
   <tbody>
      <tr>
         <th >subject1</th>
         <td>10</td>
         <td>0</td>
         <td>0</td>
      </tr>
      <tr>
         <th >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

CodePudding user response:

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'))
console.log('before:', arr)
let arrOrdered = sort2dArrayColumsByFirstRow(arr);
console.log('after:', arrOrdered);
arrayToTable(arrOrdered[0], arrOrdered.slice(1))
<table id="tableEl">
  <thead>
    <tr>
      <th >Subjects</th>
      <th>n1</th>
      <th>n2</th>
      <th>n3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th >subject1</th>
      <td>10</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr>
      <th >subject2</th>
      <td>0</td>
      <td>5</td>
      <td>20</td>
    </tr>
  </tbody>
</table>

CodePudding user response:

This is a good DOM question.

Tables are modified by the TABLE API. https://html.spec.whatwg.org/multipage/tables.html

The TABLE element has THEAD, TFOOT, and TBODY elements. Use of these elements provides structure for your javascript. (Good job so far).

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

Next, you'll need some javascript.

You'll also find insertBefore, and possibly before, and after Element methods handy.

https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore

  1. Get the TBODY element.
  2. For each row, reorder(cell[i], cell[j]).

Let's start with

function resortTBody(tBody) {
  const rows = tBody.rows;
  for(let i = 0; i < tBody.rows.length; i  ) {
    reorderRow(rows[i]);
  }
}


function reorderRow(row) {
  let cells = row.cells;
  row.insertBefore(cells[2], cells[1]);
}

This code has a hard-coded swap of cells. To reorder the cells to match a specific order, you'll need to modify reorderRow:

reorderRow(row, newOrder);

The TH's can be similarly reordered.

Design Notes: It's a good idea to minimize scope of identifiers. That is, put them in scope only as broad as it can be maximally justified.

If reorderRow is only needed for resortTbody, it can be restricted to private access.

let resortTBody = function(tBody) {
  function resortTBodyInner(tBody) {
    const rows = tBody.rows;
    for(let i = 0; i < tBody.rows.length; i  ) {
      reorderRow(rows[i]);
    }
  }

  function reorderRow(row) {
    let cells = row.cells;
    row.insertBefore(cells[2], cells[1]);
  }
  resortTBodyInner(tBody);
  resortTBody = resortTBodyInner;
};

It might be desirable to maintain the column headers but resort their contents. That would require a subtle change to the approach.

It might be desirable to reset the table to its original state. All of that can be done.

CodePudding user response:

The following one-liner will reorganize the columns in the desired order:

document.querySelectorAll("#tableEl tr").forEach(tr=>[...tr.children].forEach((_,i,a)=>tr.append(a[[0,2,1,3][i]])));
<table id="tableEl">
  <thead>
    <tr>
      <th >Subjects</th>
      <th>n1</th>
      <th>n2</th>
      <th>n3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th >subject1</th>
      <td>10</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr>
      <th >subject2</th>
      <td>0</td>
      <td>5</td>
      <td>20</td>
    </tr>
  </tbody>
</table>

CodePudding user response:

reversing the row with flex-direction:row-reverse

let btn = document.querySelector(".flip");
let row = document.querySelectorAll(".row");

btn.addEventListener("click", () => {
  row.forEach((element) => {
    element.classList.toggle("flip");
  });
});
.row {
  display: flex;
  flex-direction: row;
}

.row.flip {
  flex-direction: row-reverse;
}

td,
th {
  width: 100px;
  height: 50px;
  border: solid 1px black;
  text-align: center;
}

table {
  border: solid 1px black;
}
<table>
  <thead>
    <tr >
      <th >Subjects</th>
      <th>n1</th>
      <th>n2</th>
      <th>n3</th>
    </tr>
  </thead>
  <tbody>
    <tr >
      <th >subject1</th>
      <td>10</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr >
      <th >subject2</th>
      <td>0</td>
      <td>5</td>
      <td>20</td>
    </tr>
  </tbody>
</table>
<button >Flip</button>

  • Related