skip to Main Content

I’m having an issue with mix-blend-mode in my HTML/CSS table. I want to highlight rows and columns in a table when hovering over cells, but the mix-blend-mode effect only works as expected for rows, not columns.

I applied mix-blend-mode to cells with a .filled class for both rows and columns. I expected the blending effect to work for both highlighted rows and columns. The blending effect works for rows but not for columns. The column highlighting does not show the expected color blending.

const table = document.getElementById('scores-table');
const rows = table.getElementsByTagName('tr');

table.addEventListener('mouseover', function(e) {
  const target = e.target;
  if (target.tagName === 'TD') {
    const cellIndex = target.cellIndex;
    const rowIndex = target.parentElement.rowIndex;

    // Remove previous highlights
    Array.from(rows).forEach(row => {
      Array.from(row.children).forEach(cell => {
        cell.classList.remove('highlighted-column');
      });
      row.classList.remove('highlighted-row');
    });

    // Highlight column
    Array.from(rows).forEach(row => {
      if (row.children[cellIndex]) {
        row.children[cellIndex].classList.add('highlighted-column');
      }
    });

    // Highlight row
    if (rows[rowIndex]) {
      rows[rowIndex].classList.add('highlighted-row');
    }
  }
});

table.addEventListener('mouseout', function(e) {
  const target = e.target;
  if (target.tagName === 'TD') {
    // Remove column and row highlight
    Array.from(rows).forEach(row => {
      Array.from(row.children).forEach(cell => {
        cell.classList.remove('highlighted-column');
      });
      row.classList.remove('highlighted-row');
    });
  }
});
table {
  border-collapse: collapse;
  width: 100%;
  margin: 0 auto;
}

th,
td {
  width: 30px;
  height: 20px;
  text-align: center;
  border: 1px solid #ccc;
  /* Border for visibility */
  position: relative;
  z-index: 0;
}

.highlighted-column {
  background-color: rgba(211, 211, 211, 0.3);
  /* Light grey for column highlighting */
  mix-blend-mode: color;
}

.highlighted-row {
  background-color: rgba(211, 211, 211, 0.3);
  /* Light yellow for row highlighting */
}

td.filled {
  background-color: green;
  mix-blend-mode: color;
  position: relative;
}
<table id="scores-table">
  <tbody>
    <tr>
      <td class="">1</td>
      <td class="filled">2</td>
      <td class="filled">3</td>
    </tr>
    <tr>
      <td class="">4</td>
      <td class="">5</td>
      <td class="">6</td>
    </tr>
    <tr>
      <td class="filled">7</td>
      <td class="filled">8</td>
      <td class="">9</td>
    </tr>
  </tbody>
</table>

2

Answers


  1. The mix-blend-mode:color works fine. It is important to know that for the same CSS property set on the same element, one will overwrite one by another (based on Specificity.)

    The problem here is that on td the new background-color added by highlighted-coloumns is overwritten by the original background color from td.filled. Hence, there is no two different background colors for the mix-blend-mode to work.

    It works on the rows because the highlighted-row’s background color is applied to the row instead of the cells.

    You can either simply use a same-size td::before psuedo element and instead apply the gray color to it, or using background-blend-mode:luminosity (opposite of color blend mode) as shown below to get the exact same result.

    td.highlighted-column,
    .highlighted-row td {
        background-image: linear-gradient(rgba(211,211,211,0.3), rgba(211,211,211,0.3));
        background-blend-mode: luminosity;
    }
    
    const table = document.getElementById('scores-table');
    const rows = table.getElementsByTagName('tr');
    
    table.addEventListener('mouseover', function(e) {
      const target = e.target;
      if (target.tagName === 'TD') {
        const cellIndex = target.cellIndex;
        const rowIndex = target.parentElement.rowIndex;
    
        // Remove previous highlights
        Array.from(rows).forEach(row => {
          Array.from(row.children).forEach(cell => {
            cell.classList.remove('highlighted-column');
          });
          row.classList.remove('highlighted-row');
        });
    
        // Highlight column
        Array.from(rows).forEach(row => {
          if (row.children[cellIndex]) {
            row.children[cellIndex].classList.add('highlighted-column');
          }
        });
    
        // Highlight row
        if (rows[rowIndex]) {
          rows[rowIndex].classList.add('highlighted-row');
        }
      }
    });
    
    table.addEventListener('mouseout', function(e) {
      const target = e.target;
      if (target.tagName === 'TD') {
        // Remove column and row highlight
        Array.from(rows).forEach(row => {
          Array.from(row.children).forEach(cell => {
            cell.classList.remove('highlighted-column');
          });
          row.classList.remove('highlighted-row');
        });
      }
    });
    table {
      border-collapse: collapse;
      width: 100%;
      margin: 0 auto;
    }
    
    th,
    td {
      width: 30px;
      height: 20px;
      text-align: center;
      border: 1px solid #ccc;
      /* Border for visibility */
      position: relative;
      z-index: 0;
    }
    
    td.filled {
      background-color: green;
      position: relative;
    }
    
    td.highlighted-column,
    .highlighted-row td {
      background-image: linear-gradient(rgba(211,211,211,0.3), rgba(211,211,211,0.3));
      background-blend-mode: luminosity;
    }
    <table id="scores-table">
      <tbody>
        <tr>
          <td class="">1</td>
          <td class="filled">2</td>
          <td class="filled">3</td>
        </tr>
        <tr>
          <td class="">4</td>
          <td class="">5</td>
          <td class="">6</td>
        </tr>
        <tr>
          <td class="filled">7</td>
          <td class="filled">8</td>
          <td class="">9</td>
        </tr>
      </tbody>
    </table>

    That being said, if the JS mouse events are only for this coloring purpose, there are definetly pure CSS solutions that may or may not be simpler depending on your actual use case. (E.g. Using a long pseudo element, or using a bunch of :has when the table has a fixed and small amount of rows and columns.)

    Login or Signup to reply.
  2. Here are some solutions:

    1. Make sure that the z-index for .highlighted-column is higher than that for .highlighted-row but lower than that of the .filled cells.

    2. Add z-index properties to control the stacking context for the highlighted elements.

    adjust the blending mode for the .highlighted-row and .highlighted-column classes.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search