Bug

repeat rendering input text(first text is rendered by browser default action, last text is rendered by slate)
just heppen when user move cursor from right to left by arrowLeft
https://www.slatejs.org/examples/richtext
Slate: 0.58.1
Browser: Chrome
OS: Mac
prevent browser default action.
chrome browser think the text should follow previous node when cursor is at end edge for bold text, but slate think it should follow next node
At the first glance, I thought this problem was caused by the Slate's internal selection function.
In Safari, the editor's selection's path and anchor is correct after hitting <LeftArrow> that triggers the onDOMSelectionChange.
// editor's selection in Safari
{anchor: {path: [0, 1], offset: 4}, focus: {path: [0, 1], offset: 4}}
// editor's selection in Chrome
{anchor: {path: [0, 2], offset: 0}, focus: {path: [0, 2], offset: 0}}
This is because window.getSelection() has returned different result in Chrome and Safari.
As the selection for Editor.insertText is not correct in Chrome, my workaround is to use setTimeout to wait until the selection is updated by onDOMSelectionChange.
// slate-react/src/components/editable.tsx
...
if (!IS_SAFARI && !IS_FIREFOX && event.data) {
const data = event.data
// insert text in next event cycle
setTimeout(() => {
const { selection } = editor
if (selection) {
Transforms.setSelection(editor, {
anchor: {
path: selection.anchor.path,
offset: selection.anchor.offset - data.length,
},
focus: {
path: selection.anchor.path,
offset: selection.anchor.offset - data.length,
},
})
Editor.insertText(editor, data)
}
})
...
And it works. I also combined this with #3626 to fix cursor misplacement in Safari.

At the first glance, I thought this problem was caused by the Slate's internal selection function.
In Safari, the editor'sselection'spathandanchoris correct after hitting<LeftArrow>that triggers theonDOMSelectionChange.// editor's selection in Safari {anchor: {path: [0, 1], offset: 4}, focus: {path: [0, 1], offset: 4}} // editor's selection in Chrome {anchor: {path: [0, 2], offset: 0}, focus: {path: [0, 2], offset: 0}}This is because
window.getSelection()has returned different result in Chrome and Safari.As the selection for
Editor.insertTextis not correct in Chrome, my workaround is to usesetTimeoutto wait until the selection is updated byonDOMSelectionChange.// slate-react/src/components/editable.tsx ... if (!IS_SAFARI && !IS_FIREFOX && event.data) { const data = event.data // insert text in next event cycle setTimeout(() => { const { selection } = editor if (selection) { Transforms.setSelection(editor, { anchor: { path: selection.anchor.path, offset: selection.anchor.offset - data.length, }, focus: { path: selection.anchor.path, offset: selection.anchor.offset - data.length, }, }) Editor.insertText(editor, data) } }) ...And it works. I also combined this with #3626 to fix cursor misplacement in Safari.
你好,这个方法确实能够解决这个中文输入重复的问题,可是在一段普通文本中先点击加粗,然后再输入文本,此时输入是否会因此导致输入为普通不带格式的文本呢?
It's contenteditable problem in React. You can insert a '\ufeff' after add or remove mark when cursor is collapsed
@wangqs1990 I write the slate editor by angular ,same problem, so this is a compatibility issue for Chrome
I get this behavior too, when using Swiftkey keyboard on Android. Other keyboards works fine.
In my opinion, this is caused by react reused the previous span and didn't update it's value. Here is the exactly reason:
span1), the added text will be added to current span(span1) directly because of span's editable=true. However the slate element(e.g.element1) , current span associated with, didn't change anything!onCompositionEnd,slate-react will create a new Text node with the event's data.And then slate will call editor.onChange to start the react rerender procedure.react rerendering, react think element1 didn't change, so react reuse the span1 and no need do anything. As the result, react create the new span(e.g.span2) for element2 and remain span1(assocated with element1).I also meet the problem before, to simplify this problem ,I tried to tell react don't reuse the decorated span ,so I tried this and run away from the problem.
const createAlwaysNewComponent = () => ({ attributes, children }) => (
<span
{...attributes}
css={css`
color: var(--theme-color-text-lighten, #33333380);
`}
>
{children}
</span>
);
const DecoratedSpan = ({ attributes, children, leaf }) => {
const AlwaysNew = createAlwaysNewComponent();
return <AlwaysNew attributes={attributes}>{children}</AlwaysNew>;
};
在chrome浏览器上,这大概率是由于react复用之前的span元素导致的。具体过程为:
1.在decorated的节点span1后键入中文字符,由于editable=ture的效果,实际的文本会直接增加在当前span元素内部。注意,此时,与之对应的slate element并没有发生任何变化。
2.在确定输入后,slate通过onCompositionEnd事件知道键入内容并创立了一个新的span2,并将更新内容同步到slate value上。
3.根据react框架,span1对应的slate element并没有发生变化,故react不会更新span1(这导致前一段文本没有删除),而span2作为新增节点,react创建并绘制了新的节点。
综上,这导致了重复文本现象。
@ncqwer 试了还是不行啊~
Most helpful comment
In my opinion, this is caused by
reactreused the previousspanand didn't update it's value. Here is the exactly reason:span1), the added text will be added to currentspan(span1) directly because of span'seditable=true. However the slate element(e.g.element1) , current span associated with, didn't change anything!onCompositionEnd,slate-reactwill create a new Text node with the event's data.And thenslatewill calleditor.onChangeto start thereactrerender procedure.reactrerendering,reactthinkelement1didn't change, so react reuse the span1 and no need do anything. As the result, react create the new span(e.g.span2) forelement2and remainspan1(assocated withelement1).After all you will see the repeat render.
I also meet the problem before, to simplify this problem ,I tried to tell
reactdon't reuse the decoratedspan,so I tried this and run away from the problem.在chrome浏览器上,这大概率是由于
react复用之前的span元素导致的。具体过程为:1.在decorated的节点
span1后键入中文字符,由于editable=ture的效果,实际的文本会直接增加在当前span元素内部。注意,此时,与之对应的slate element并没有发生任何变化。2.在确定输入后,
slate通过onCompositionEnd事件知道键入内容并创立了一个新的span2,并将更新内容同步到slate value上。3.根据react框架,
span1对应的slate element并没有发生变化,故react不会更新span1(这导致前一段文本没有删除),而span2作为新增节点,react创建并绘制了新的节点。综上,这导致了重复文本现象。