Mark cells with changes in HTML Table

Why such a code? I’m displaying the history of the database record – in a table, each row is a separate record of the history. The highest row contains current record values, each subsequent is an older version of the same record. It is useful when you have a system where various persons or applications can make changes to the data, and you want to track what changed. Here is how it may look like:

Again – the first row contains current data, so there is nothing to mark, the second row contains the previous version of the record. In the previous version, the First name and Phone were different.

JavaScript code

In order to mark records in the table, I used simple JavaScript functions and CSS styles. Here is the code:

function markDifferences() {
  Array.from(document.getElementsByClassName('pure-table')).forEach(sDiv => {
    Array.from(sDiv.getElementsByTagName('tbody')).forEach(sTable => {
      let sRowsArray = Array.from(sTable.getElementsByTagName('tr'));
      if (sRowsArray.length > 1) {
        for (let i=1; i<sRowsArray.length; i++) {
          compareRows(sRowsArray[i-1], sRowsArray[i]);
        }
      }
    });
  });
}

function compareRows(prev, curr) {
  let pCells = Array.from(prev.getElementsByTagName('td'));
  let cCells = Array.from(curr.getElementsByTagName('td'));
  if (cCells.length > 1) {
    for (let i=0; i<cCells.length; i++) {
      if (pCells[i].innerHTML != cCells[i].innerHTML) {
        cCells[i].classList.add('different');
      }
    }
  }
}

markDifferences();

The “markDifferences” function takes all elements with the class of ‘pure-table’ and looks for ‘tbody’ element in them. For each table body, rows are collected and if there is more than one row, the loop is called to compare rows. There is no need to run the code for a single row, and we can start comparing from the second row.

The “compareRows” function takes “previous” and “current” rows as arguments, finds all cells in these rows, and compares content cell by cell. The cell that is different from the corresponding cell above is marked by the CSS class (‘different’). Now you only need to style such cells properly to make them stand out visually.

Sometimes there is also a need to remove rows that contain no changes (there was no change to the record, but the history entry is saved). In such a case, I’m this slightly modified code:

function markDifferences() {
  Array.from(document.getElementsByClassName('pure-table')).forEach(sDiv => {
    Array.from(sDiv.getElementsByTagName('tbody')).forEach(sTable => {
      let sRowsArray = Array.from(sTable.getElementsByTagName('tr'));
      if (sRowsArray.length > 1) {
        for (let i=1; i<sRowsArray.length; i++) {
          compareRows(sRowsArray[i-1], sRowsArray[i]);
        }
        for (let i=1; i<sRowsArray.length; i++) {
          if (!Array.from(sRowsArray[i].classList).includes('contains-changes')) {
            sRowsArray[i].remove();
          }
        }
      }
    });
  });
}

function compareRows(prev, curr) {
  let pCells = Array.from(prev.getElementsByTagName('td'));
  let cCells = Array.from(curr.getElementsByTagName('td'));
  if (cCells.length > 1) {
    for (let i=0; i<cCells.length; i++) {
      if (pCells[i].innerHTML != cCells[i].innerHTML) {
        cCells[i].classList.add('different');
        curr.classList.add('contains-changes');
      }
    }
  }
}

markDifferences();

If the cell differs from the corresponding cell above, not only cell styling is adjusted, but also the style of the whole row. You can hide “unchanged” rows using CSS, but I have chosen to simply remove them from the DOM – which is performed in the additional loop in “markDifferences” function. The result for the same table looks like this: