Skip to content

fix(core): track pre-edit selection via beforeinput to stop desync - #60

Open
artnikbrothers wants to merge 1 commit into
GoncharukOrg:mainfrom
artnikbrothers:fix/59-final
Open

fix(core): track pre-edit selection via beforeinput to stop desync#60
artnikbrothers wants to merge 1 commit into
GoncharukOrg:mainfrom
artnikbrothers:fix/59-final

Conversation

@artnikbrothers

Copy link
Copy Markdown

Fast typing (or holding Backspace) can revert or duplicate characters. Root cause:

  • The pre-edit selection needed to classify each input event (insert vs delete, and the edited range) was cached only by a self-rescheduling setTimeout(0) poll.
  • Browsers throttle nested zero-delay timers to a ~4ms floor, so under fast typing (or extra work per keystroke, e.g. a form library revalidating on change) the poll falls behind the real cursor position.
  • Separately, a plain element.value = write - e.g. a controlled component re-rendering with a reformatted value - moves the cursor to the end without firing input or updating the cached selection.
  • Either way the cached selection goes stale, so the next keystroke's addedValue slice spans the wrong range and splices text back at the wrong position - producing the reverted/duplicated characters.

Fix: capture the pre-edit selection from the beforeinput event instead. It fires synchronously right before the browser applies an edit, so it always reflects the real DOM selection at that instant, including a cursor already moved by a prior controlled re-render. The existing setTimeout poll stays in place as a fallback for cases beforeinput doesn't cover (autofill, or a pure cursor move with no edit).

Fixes #59

The pre-edit selection needed to classify each "input" event (insert vs
delete, and the edited range) was cached only by a self-rescheduling
setTimeout(0) poll. Browsers throttle nested zero-delay timers to a ~4ms
floor, so under fast typing (or extra synchronous work per keystroke, e.g.
a form library revalidating on change) the poll falls behind the real
cursor position. Separately, a plain `element.value =` write - such as a
controlled component re-rendering with a reformatted value - moves the
native cursor to the end without firing "input" or updating the cached
selection. Either path leaves the cached selection stale relative to the
DOM, so the next keystroke's addedValue slice spans the wrong range and
splices text back at the wrong position, producing reverted or duplicated
characters.

Capture the pre-edit selection from the "beforeinput" event instead, which
fires synchronously right before the browser applies an edit and always
reflects the real DOM selection at that instant - including a cursor
already moved by a prior controlled re-render. The existing setTimeout
poll is left in place as a fallback for cases beforeinput doesn't cover
(autofill, or pure cursor moves with no edit).

Fixes GoncharukOrg#59
@artnikbrothers

Copy link
Copy Markdown
Author

Hi @GoncharukBro, any chance we can review this PR and if all good release it? we have a problem in our applications because of this issue, and would really appreciate if we can fix it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fast typing / held Backspace causes reverted or duplicated characters (setTimeout(0) selection-tracking race)

2 participants