Description
Repro:
<input bind-value-oninput="@SomeString" />
- Type
X
into the input - Position the cursor at the start (before the
X
) - Mash the keyboard
Expected: Keystrokes typed should appear only at the cursor position, so the X
should remain at the end
Actual: A few keystrokes appear before the X
, but then the cursor jumps to the end, and subsequent keystrokes are placed after the X
I think this happens because of how bind
does a 2-way binding. On each keystroke, it not only (1) reads the updated value and writes it to SomeString
, but also (2) tries to re-apply the value from SomeString
to the input. When it does (2), as long as the value in the input already matches the string, it's a no-op and gets skipped, so normally we don't do anything that moves the cursor.
However, because of the asynchrony with Razor Components, there might be more keystrokes between (1) and (2), so the value we're applying in (2) no longer matches the contents of the input. We don't lose the keystrokes (we already did some work on that), but the fact that input.value !== SomeString
means we do write both the previous and then current value to the input. The fact that we're no longer no-opping this write means now the cursor implicitly moves to the end.
Suggested fix: In BrowserRenderer.ts
, in tryApplyValueProperty
, we should add more logic to preserve the selectionStart/selectionEnd properties of input/textarea when assigning value. This would also improve things in other scenarios, like if you had a collaborative textarea bound to a value that multiple users could edit simultaneously.