Border around specific set of cells

Viewed 112

I have done the component with table like this.

The problem is, when user chooses a range of cell like this, how can i put the border around that groups( the border likes the first one in group of cells) ?

Pic1

I tried to use different class for each cell's border style, but it seems imposible, because i dont know which cell user will pick, how big the group of cell's size will be

Thank a lot and sorry about my English

1 Answers

Had the same task this week and sadly the question did not hold any answer, except for external libraries. So here is my approach.

In the following example I am using the class Marked to mark the cells as being selected and the dataset to flag which cells define which outer bound(s).

;window.onload = (event) => {
  //REM: Assign mousedown event  
  document.querySelector('table').addEventListener('mousedown',_onMouseDownTable, false)
};

/***
* MouseDown event for the table, handles cells
*/
function _onMouseDownTable(event){
  //REM: Checking for 'td'...
  if(
    event.target &&
    event.target.tagName.toLowerCase() === 'td'
  ){
    const tObject = {Down: event.target};

    //REM: ...add event for marking cells
    this.onmousemove = ((object, event) => {
      if(
        event.target &&
        event.target.tagName.toLowerCase() === 'td'
      ){
        object.Move = event.target
      }
      else{
        delete object.Move
      };

      _markCells(object.Down, object.Move)
    }).bind(null, tObject);

    //REM: ...add event for finish marking cells
    this.onmouseleave = this.onmouseup = ((object, event) => {
      this.onmouseleave = this.onmousemove = this.onmouseup = null;
    }).bind(null, tObject)
  }
};

/***
* Marks cells between cell a and cell b
*/
function _markCells(a, b){
  if(a && b){
    //REM: Table of a (thus usually b)
    const tTable = a.closest('table');

    //REM: Remove existing marks and border data
    tTable.querySelectorAll('td.Marked, td[data-bounds-left], td[data-bounds-right], td[data-bounds-top], td[data-bounds-bottom]').forEach((td) => {
      td.classList.remove('Marked');

      delete td.dataset.boundsLeft;
      delete td.dataset.boundsRight;
      delete td.dataset.boundsTop;
      delete td.dataset.boundsBottom
    });

    //REM: The lowest (top to bottom / left to right) and the largest coordinate
    const
      tMaxColumn = Math.max(Number(a.dataset.columnIndex), Number(b.dataset.columnIndex)),
      tMaxRow = Math.max(Number(a.dataset.rowIndex), Number(b.dataset.rowIndex)),
      tMinColumn = Math.min(Number(a.dataset.columnIndex), Number(b.dataset.columnIndex)),
      tMinRow = Math.min(Number(a.dataset.rowIndex), Number(b.dataset.rowIndex));

    //REM: Mark all cells between Min and Max
    for(let row = tMinRow; row <= tMaxRow; row++){
      for(let column = tMinColumn; column <= tMaxColumn; column++){
        const tCell = tTable.querySelector(`td[data-row-index='${row}'][data-column-index='${column}']`);
        if(tCell){
          //REM: If outer left bound...
          if(column === tMinColumn){
            tCell.dataset.boundsLeft = true
          };

          //REM: If outer right bound...
          if(column === tMaxColumn){
            tCell.dataset.boundsRight = true
          };

          //REM: If outer top bound...
          if(row === tMinRow){
            tCell.dataset.boundsTop = true
          };

          //REM: If outer bottom bound...
          if(row === tMaxRow){
            tCell.dataset.boundsBottom = true
          };

          //REM: Set cell as marked
          tCell.classList.add('Marked')
        }
      }
    }
  }
};
td{
  border: 2px solid #000;
  background-color: grey;
  height: 25px;
  width: 200px;
}

td.Marked{
  background-color: #1e90ff
}

td.Marked[data-bounds-left]{
    border-left-color: red
}

td.Marked[data-bounds-right]{
    border-right-color: red
}

td.Marked[data-bounds-top]{
    border-top-color: red
}

td.Marked[data-bounds-bottom]{
    border-bottom-color: red
}
<table>
  <tbody>
    <tr>
      <td data-column-index="0" data-row-index="0"></td>
      <td data-column-index="1" data-row-index="0"></td>
      <td data-column-index="2" data-row-index="0"></td>
      <td data-column-index="3" data-row-index="0"></td>
      <td data-column-index="4" data-row-index="0"></td>
    </tr>
    <tr>
      <td data-column-index="0" data-row-index="1"></td>
      <td data-column-index="1" data-row-index="1"></td>
      <td data-column-index="2" data-row-index="1"></td>
      <td data-column-index="3" data-row-index="1"></td>
      <td data-column-index="4" data-row-index="1"></td>
    </tr>
    <tr>
      <td data-column-index="0" data-row-index="2"></td>
      <td data-column-index="1" data-row-index="2"></td>
      <td data-column-index="2" data-row-index="2"></td>
      <td data-column-index="3" data-row-index="2"></td>
      <td data-column-index="4" data-row-index="2"></td>
    </tr>
    <tr>
      <td data-column-index="0" data-row-index="3"></td>
      <td data-column-index="1" data-row-index="3"></td>
      <td data-column-index="2" data-row-index="3"></td>
      <td data-column-index="3" data-row-index="3"></td>
      <td data-column-index="4" data-row-index="3"></td>
    </tr>
    <tr>
      <td data-column-index="0" data-row-index="4"></td>
      <td data-column-index="1" data-row-index="4"></td>
      <td data-column-index="2" data-row-index="4"></td>
      <td data-column-index="3" data-row-index="4"></td>
      <td data-column-index="4" data-row-index="4"></td>
    </tr>
  </tbody>
</table>

Note that this is just a draft and not a finished solution, since it depends heavily on the already existing implementation and use-case.

Related