Svelte contenteditable is not updated properly

Viewed 281

I created spreadsheet with Svelte, but it seems like it won't update the cell properly.

I appended the "2" postfix to every cell, and when I edit the content (say to fix first letter in jim to be uppercased) it should end up with two "2" as "Jim22". But it end up with just one "2" as "Jim2".

I also have JSON output to inspect the value of rows, and it changed, so the component was updated correctly, but the editable cell was not updated to "Jim22" it still "Jim2".

Why, and how to fix that?

playground

enter image description here

Code

<script>
  let rows = [
    ["jim"],
    ["Kate"]
  ]

  function update(i, j, event) {
        rows[i][j] = event.target.innerText
  }
</script>

<table>
  {#each rows as row, i}
    <tr>
      {#each row as cell, j}
        <td contenteditable="true" on:blur="{(event) => update(i, j, event)}">{cell}2</td>
      {/each}
    </tr>
  {/each}
</table>

<pre>{JSON.stringify(rows, null, 2)}</pre>
2 Answers

Just add the key for each statement. according to document

<script>
  let rows = [
    ["Jim",  "Raynor"],
    ["Kate", "Bishop"]
  ]

  function update(i, j, event) {
        rows[i][j] = event.target.innerText
  }
</script>

<table>
  {#each rows as row, i (row)}
    <tr>
      {#each row as cell, j (cell)}
        <td contenteditable="true" on:blur="{(event) => update(i, j, event)}">{cell}2</td>
      {/each}
    </tr>
  {/each}
</table>

<pre>{JSON.stringify(rows, null, 2)}</pre>

I don't think Long Vu's answer is the root cause of the problem, because it cannot explain why the modification mentioned in the comments of the question works. Adding keys is rather a workaround.

After some debugging and searching, I found this issue: Repeat words in plain text aren't reactive. When trying to update "Jim" to "Jim2", svelte uses set_data function, which compares text.wholeText === data. While text.data is "Jim", text.wholeText and data are both "Jim2", so the update is skipped.

About wholeText:

The Text.wholeText read-only property returns the full text of all Text nodes logically adjacent to the node.

So, by inspecting childNodes property of td:

  • <td>{cell}2</td> has two Text nodes: "Jim" and "2", and wholeText is "Jim2"
  • <td>{cell + "2"}</td> has one Text node, no problem
  • <td>{@html cell}2</td> is not using set_data function, fine

Also as said in the issue, downgrading svelte to v3.23.2 makes <td>{cell}2</td> work as expected.

Related