0

In my example example I like to capture a keydown event inside an editable table:

  1. Click/select a cell
  2. Press delete
<!-- index.html -->
    <table contenteditable="true">
        <tr>
            <td>Cell 1</td>
            <td>Cell 2</td>
            <td>Cell 3</td>
        </tr>
    </table>

When I use for example a simple simple click event it works fine:

    //script.js
    const tdElements = document.querySelectorAll("td");
    function clickHandler(){this.remove()}
    tdElements.forEach((item)=>{item.addEventListener("click", this.clickHandler)});

But when I try to catch the delete key event, only the cell gets edited (the keydown event is not catched). I suppose the reason for this is that the keyevents for editing the cell is overriding my added event listener. How can make the event listener to catch the delete key command?

    //script.js
    const tdElements = document.querySelectorAll("td");
    function keydownHandler() {console.log(event.key)}; //console.log does not work
    tdElements.forEach((item)=>{item.addEventListener("keydown", keydownHandler)})

When I simply add the key catcher to the document the key gets logged as expected.

    //script.js
    function keydownHandler() {console.log(event.key)}; //console.log works fine
    document.addEventListener("keydown", keydownHandler)
KeepRunning85
  • 173
  • 10
  • 1
    You did not pass the event to your function so it is undefined. Read up on arrow functions a bit https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#cannot_be_used_as_methods – Mark Schultheiss Aug 07 '23 at 19:26
  • ONE example https://stackoverflow.com/a/50101839/125981 there are others – Mark Schultheiss Aug 07 '23 at 19:37

2 Answers2

2

Problem

You set an event listener for the keydown event on all your td elements however these elements don't generate keyboard events. From MDN documentation:

Keyboard events are only generated by <input>, <textarea>, <summary> and anything with the contentEditable or tabindex attribute. If not caught, they bubble up the DOM tree until they reach Document.

In your case, the table element would generate the event. Demo below:

const tdElements = document.querySelectorAll("td");

function keydownHandler() {
  console.log(event.key);
}

document
  .getElementById("table")
  .addEventListener("keydown", keydownHandler);

tdElements.forEach((item) => {
  item.addEventListener("keydown", keydownHandler);
});
<table id="table" contenteditable="true">
  <tr>
    <td>Cell 1</td>
    <td>Cell 2</td>
    <td>Cell 3</td>
  </tr>
</table>

Solution

You could add the tabindex attribute to each of your tds. This will allow them to emit the keydown event. There's the additional benefit that these are now navigable via the keyboard. Demo below:

const tdElements = document.querySelectorAll("td");

function keydownHandler() {
  console.log(event.key);
}

tdElements.forEach((item) => {
  item.addEventListener("keydown", keydownHandler);
});
<table id="table" contenteditable="true">
  <tr>
    <td tabindex="0">Cell 1</td>
    <td tabindex="0">Cell 2</td>
    <td tabindex="0">Cell 3</td>
  </tr>
</table>
Wing
  • 8,438
  • 4
  • 37
  • 46
  • 1
    Did you test your solution? It doesn't work for me on Chrome – imvain2 Aug 07 '23 at 18:56
  • Huh, thanks for pointing that out. I've only tested in Firefox. Will see what's happening in Chrome. – Wing Aug 07 '23 at 18:57
  • It looks like in Chrome when you click to edit a cell it places its focus on the table and not the cell. As a result, keyboard events still get sent from the `table` element. – Wing Aug 07 '23 at 19:01
  • @Wing Many thanks for your answer. It is a valueable explanation! – KeepRunning85 Aug 07 '23 at 19:27
  • @KeepRunning85: thanks! Do note the above comments regarding it not working in Chrome. I'll see what I can do to fix it so please keep an eye out for an update. – Wing Aug 07 '23 at 19:43
  • While doing some more testing, I've found tabbing through the cells doesn't move the caret which makes for a bad experience. – Wing Aug 07 '23 at 19:57
  • I'm going to come back to this later because I have other things I need to do. – Wing Aug 07 '23 at 20:00
0

After investigation I found a way to delete certain cells by selecting and afterwards pressing delete. By adding an event listener to the document it finally works.
Still I dont know why the event listeners don't work for the td-elements.

    <!-- index.html -->
    <table contenteditable="true">
        <tr>
            <td>Cell 1</td>
            <td>Cell 2</td>
            <td>Cell 3</td>
        </tr>
    </table>
    //script.js
    const tdElements = document.querySelectorAll("td");
    var selectedCell;

    //event handlers
    function selectCell() {
        selectedCell = this; //assign selected cell
        console.log(selectedCell)};
    function deleteCell() {
        if(event.key == "Delete"){ //catch the delete event
        selectedCell.remove()}; 
        event.preventDefault()} //prevent the event from doing further text changes

    //Assigning event handlers
    tdElements.forEach(td => td.addEventListener("click", selectCell));
    document.addEventListener("keydown", deleteCell);
KeepRunning85
  • 173
  • 10