karaxnim/karax

Empty content editable raises an error

Patitotective opened this issue · 1 comments

include karax/prelude
import karax/kdom

proc render(): VNode = 
  buildHtml(tdiv):
    span(contenteditable = true):
      text "hello"

setRenderer render

If you compile the code above, focus the span and empty it (by backspacing or Ctrl+A backspace/delete) you'll get the following error:

Uncaught Error: Error: unhandled exception: /home/cristobal/.nimble/pkgs/karax-1.3.0/karax/karax.nim(674, 16) `same(kxi.currentTree, document.getElementById(kxi.rootId))`  [AssertionDefect]
Traceback (most recent call last)
/home/cristobal/.nimble/pkgs/karax-1.3.0/karax/karax.nim(710) at redraw.:anonymous
/home/cristobal/.nimble/pkgs/karax-1.3.0/karax/karax.nim(674) at karax.dodraw
/home/cristobal/.choosenim/toolchains/nim-1.6.12/lib/system/assertions.nim(38) at assertions.failedAssertImpl
/home/cristobal/.choosenim/toolchains/nim-1.6.12/lib/system/assertions.nim(28) at assertions.raiseAssert
/home/cristobal/.choosenim/toolchains/nim-1.6.12/lib/system/fatal.nim(54) at sysFatal.sysFatal
    unhandledException file:///home/cristobal/dev/test/app.js:5440
    raiseException file:///home/cristobal/dev/test/app.js:1271
    sysFatal_218103842 file:///home/cristobal/dev/test/app.js:5478
    raiseAssert_218103840 file:///home/cristobal/dev/test/app.js:5488
    failedAssertImpl_218103864 file:///home/cristobal/dev/test/app.js:5498
    dodraw_620757930 file:///home/cristobal/dev/test/app.js:7183
    HEX3Aanonymous_620757989 file:///home/cristobal/dev/test/app.js:7248
app.js:5440:11

From what I figured this happens because the virtual node's value stays the same while the actual node's value is modified, so when you empty the contenteditable, the real node.childNodes's also becomes empty, thus causing https://github.com/karaxnim/karax/blob/master/karax/karax.nim#L234 to fail.
A workaround I found was to store the real node.childNodes[0] in a variable before the content editable empties, and when it does we use appendChild to add it again but we change .data to an empty string, here's a demostration:

include karax/prelude
import karax/kdom

var tempChild: Node

proc render(): VNode = 
  buildHtml(tdiv):
    span(id = "input", contenteditable = true):
      text "hello"

      proc onkeydown(event: Event, node: VNode) = 
        let element = getElementById(node.id)
        if element.childNodes.len == 1:
          tempChild = element.childNodes[0]

      proc oninput(event: Event, node: VNode) = 
        let element = getElementById(node.id)
        if element.len == 0:
          tempChild.data = ""
          element.appendChild(tempChild)

setRenderer render

Karax version 1.3.0 - Ubuntu 22.04.2 LTS

geotre commented

There are probably a lot of edge cases with contentEditable. Your workaround seems reasonable but I'm not sure how we'd integrate it into Karax to avoid this. Might be worth looking into how other frameworks do it