Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

onInput not fired when typing at the start of a contenteditable #5603

Open
user178392143 opened this issue Feb 7, 2024 · 4 comments
Open
Labels

Comments

@user178392143
Copy link

Description
In Slate 0.101.5 and Slate-react 0.101.5, a character typed at the start (i.e. offset 0) of a contenteditable does not produce an onInput event.

As far as I can tell, this is due to the check in onDOMBeforeInput at:

// Chrome has issues correctly editing the start of nodes: https://bugs.chromium.org/p/chromium/issues/detail?id=1249405
// When there is an inline element, e.g. a link, and you select
// right after it (the start of the next node).
selection.anchor.offset !== 0

This is part of the conditional looking for native insertText events. When the cursor is at offset 0, this fails, and the event is not flagged as native (though it is a native event), causing event.preventDefault() to get called at:

if (!native) {
event.preventDefault()
}

That prevents onInput from firing.

I think this seems unintentional? The linked Chrome issue in that comment seems to be discussing a similar but unrelated issue with cursor position restoration. It looks like the check that works around that bug is what's causing the issue I'm seeing.

Recording
In Chrome 121.0.6167.86, 64-bit, on Windows 11. I've added event handlers logging onDOMBeforeInput and onInput, and show the difference between typing in the middle of the text and at the beginning.

slate-0-101-5-no-oninput.mp4

Sandbox
I apologize, but I haven't been able to get JSFiddle working with Slate 0.101.5. If someone knows how to set that up, then https://jsfiddle.net/4wgy3j0x/1/ will repro the bug. (All that's needed is the default example with event handlers to show that onInput isn't firing.)

Steps
To reproduce the behavior:

  1. Set up the sandbox example and open the browser dev tools to the console.
  2. Place the cursor in the middle of the default text and type one character. (onDOMBeforeInput and onInput fire.)
  3. Place the cursor at the beginning of the default text and type one character. (Only onDOMBeforeInput fires.)

Expectation
I would expect onInput to fire when typing a character at any cursor position in the contenteditable.

Environment

  • Slate Version: 0.101.5
  • Operating System: Windows 10, 11
  • Browser: Chrome, Firefox

Context
This is for a change tracking feature that requires me to add marker elements to the document delimiting runs of inserted text when the user types. Thus I'm looking for an event where I can see that the user' s typed a character, and modify the document from there. onInput seems like the best candidate, but then I ran into this issue. (onDOMBeforeInput does not work to modify the document, because after the handler runs it will restore the selection to a previously-saved value that is, by then, invalid.) Any workarounds welcome!

@dylans
Copy link
Collaborator

dylans commented Feb 8, 2024

The code in question hasn't changed in 3 years, but was added to work around https://issues.chromium.org/issues/40197599.

It's possible that Chrome recently changed behavior here (as they did break us in an interesting way in Chrome 112 or so).

You'll see at the bottom of the Chrome issue is the mention of

// Put this at the start and end of an inline component to work around this Chromium bug:
// https://bugs.chromium.org/p/chromium/issues/detail?id=1249405
const InlineChromiumBugfix = () => (
<span
contentEditable={false}
className={css`
font-size: 0;
`}
>
${String.fromCodePoint(160) /* Non-breaking space */}
</span>
)
.

What I would suggest:

  • Try the test case in https://issues.chromium.org/issues/40197599 , is this still a bug in Chrome?
  • If not, then we can remove the workaround, but need to determine the Chrome version when this behavior changed.
  • If still a bug, then I suggest trying to use the workaround suggested.

@user178392143
Copy link
Author

user178392143 commented Feb 8, 2024

Thanks! As far as I can tell, the Chromium bug still reproduces in Chrome (you can't manage to type at the beginning of the <code> node.)

I gave the inline workaround a try, but it didn't change the results. One thing to note is that the onInput issue occurs equally with inline or block nodes. In my minimal example, it's a block node (a <div>) that we're editing, and with my actual application, it's an inline. With the workaround I saw the same results in both: no onInput fired.

Here's the minimal example, which shows the bug within a <div>:

slate-noinput-div-block-element-with-workaround.mp4

@dylans
Copy link
Collaborator

dylans commented Feb 9, 2024

Thanks @user178392143 for verifying. I'll take a look and see if I have any ideas, but I'm mildly pessimistic as this is often an area where fixing one thing breaks three other things.

@user178392143
Copy link
Author

I've come up with a workaround for this. I can't vouch for its total reliability, but you can listen to onDOMBeforeInput and simply save the InputEvent it gives you for later processing, such as in onValueChange. This does mean you have to make your changes to the document after Slate has processed the input, which is probably the better course anyways. This has gotten me unblocked for the time being.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants