Richard Silvertass Richard Silvertass - 6 months ago 27
HTML Question

Listen for content changes in <td contenteditable=true>

I'm having a little problem with my table. It contains empty cells that are editable. The scenario I wish to support is the following:


  • Mark a cell and type R. R should appear in the marked cell. The neighbour cell to the right should gain focus and remain empty.

  • Mark a cell and type D. D should appear in the marked cell. The neighbour cell below should gain focus and remain empty.



When I try to implement this, the letter appears in the marked cell and the neighbour gains focus. BUT the letter also appears in the neighbour cell.
This can be demonstrated in the following code snippet:



var markedCell;
var markedCellRow;
var markedCellCol;
var table = document.getElementById('myTable');
table.onclick = function tableMouseListener(event) {
markedCell = event.target;
markedCellRow = markedCell.parentNode.rowIndex;
markedCellCol = markedCell.cellIndex;
};
document.body.addEventListener('keypress', function(e) {
if (e.keyCode == 114) {
//Jump to the Right when the 'R' key is pressed
markedCell.appendChild(document.createTextNode(String.fromCharCode(e.keyCode)));
markedCellCol++;
markedCell = table.rows[markedCellRow].cells[markedCellCol];
markedCell.focus();
}
if (e.keyCode == 100) {
//Jump Down when the 'D' key is pressed
markedCell.appendChild(document.createTextNode(String.fromCharCode(e.keyCode)));
markedCellRow++;
markedCell = table.rows[markedCellRow].cells[markedCellCol];
markedCell.focus();
}
});

#myTable tr td {
width: 55px;
height: 55px;
border: 1px solid black;
text-align: center;
text-transform: capitalize;
font-size: 32px;
color: black;
}

<table id="myTable">
<tr>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
</tr>
<tr>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
</tr>
<tr>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
</tr>
<tr>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
<td contentEditable="true"></td>
</tr>
</table>





I think maybe the right solution is to listen for content changes in the cells and clear it. But I'm not sure how to do this. Or maybe I should put invisible input fields in each cell.

Thankful for any help!

Answer

You need to prevent the default action of the keypress event when you're going to fill in the content yourself:

e.preventDefault();

Updated snippet (see note after, though):

var markedCell;
var markedCellRow;
var markedCellCol;
var table = document.getElementById('myTable');
table.onclick = function tableMouseListener(event) {
  markedCell = event.target;
  markedCellRow = markedCell.parentNode.rowIndex;
  markedCellCol = markedCell.cellIndex;
};
document.body.addEventListener('keypress', function(e) {
  if (e.keyCode == 114) {
    //Jump to the Right when the 'R' key is pressed
    markedCell.appendChild(document.createTextNode(String.fromCharCode(e.keyCode)));
    markedCellCol++;
    markedCell = table.rows[markedCellRow].cells[markedCellCol];
    markedCell.focus();
    e.preventDefault();
  }
  if (e.keyCode == 100) {
    //Jump Down when the 'D' key is pressed
    markedCell.appendChild(document.createTextNode(String.fromCharCode(e.keyCode)));
    markedCellRow++;
    markedCell = table.rows[markedCellRow].cells[markedCellCol];
    markedCell.focus();
    e.preventDefault();
  }
});
#myTable tr td {
  width: 55px;
  height: 55px;
  border: 1px solid black;
  text-align: center;
  text-transform: capitalize;
  font-size: 32px;
  color: black;
}
<table id="myTable">
  <tr>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
  </tr>
  <tr>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
  </tr>
  <tr>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
  </tr>
  <tr>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
    <td contentEditable="true"></td>
  </tr>
</table>


You might also want to look at what happens when you're dealing with a d on the last row, or with an r in the rightmost column; right now, you're assuming a cell will exist below (d) or to the right (r) of the cell you're in, but of course that's not always the case.