Bug: Cannot place cursor at the end of the line (Safari)
Lexical version: 0.10.0
Steps To Reproduce
- Open Lexical Playground in Safari
- Add an example image
- Try to place the cursor at the end of the line
The current behavior
The cursor seems to be placed at the wrong position when the last node is a DecoratorNode. This happens only in Safari. This is a bit confusing, because only when the user starts typing the text is inserted at the correct position.
The expected behavior
The cursor should be placed at the end of the line.
I am seeing the same thing.
It there any fix or workaround?
This is the case with all DecoratorNode nodes apparently. I reproed in the playground with a line ending in an equation. Results:
- Safari desktop and Safari/Chrome iOS: cannot place caret at end of line. Hitting backspace with caret at the beginning of the next line makes caret move to the beginning of the offending line instead of the end.
- Chrome desktop: behaviour is fine.
This is quite a big deal for us so I put some effort into pinning down the problem, and here is what I have discovered so far.
The bug can be reproduced using this minimal HTML which mimics the DOM produced by Lexical with some text and a decorator node at the end of the line.
<div contenteditable="true" style="padding: 4px;">
<p>
<span>Hello </span>
<span contenteditable="false" style="background: lightgrey">
decorator
</span>
<br> <!-- <--- this seems to break the caret on Safari -->
</p>
<p>
<br>
</p>
</div>
Move the caret to the second line, then press the "back" arrow.
Chrome: the caret goes to the end of the first line as expected Safari: the caret goes to the beginning of the first line (but its really at the end because if you type a letter it appears in place).
Clearly this is a Safari bug, but it turns out that if we remove the <br> from the end of the the first paragraph things works as expected in both Chrome and Safari.
Is the <br> there for a good reason?
https://jsfiddle.net/3ga4k8ms/
This is quite a big deal for us so I put some effort into pinning down the problem, and here is what I have discovered so far.
The bug can be reproduced using this minimal HTML which mimics the DOM produced by Lexical with some text and a decorator node at the end of the line.
<div contenteditable="true" style="padding: 4px;"> <p> <span>Hello </span> <span contenteditable="false" style="background: lightgrey"> decorator </span> <br> <!-- <--- this seems to break the caret on Safari --> </p> <p> <br> </p> </div>Move the caret to the second line, then press the "back" arrow.
Chrome: the caret goes to the end of the first line as expected Safari: the caret goes to the beginning of the first line (but its really at the end because if you type a letter it appears in place).
Clearly this is a Safari bug, but it turns out that if we remove the
<br>from the end of the the first paragraph things works as expected in both Chrome and Safari.Is the
<br>there for a good reason?https://jsfiddle.net/3ga4k8ms/
Thanks for the investigation here. I don't know off the top of my head why that br is there in this context. It might be some general thing that we do to deal with Decorator selection that we can opt out of in this case. I'll have to look at that code. Is it still possible to select the next line below the Decorator without that br? If not, I guess you can still select after the Decorator and then just add a new line/new paragraph with Enter or whatever.
@zurfyx @fantactuka do either of you know why we put this line break after the decorator container?
Still relevant...
Core ref: https://github.com/facebook/lexical/blob/4c30b5215f0269c069c6c01f5eea973163a77321/packages/lexical/src/LexicalReconciler.ts#L337-L341
Sorry for the bump, but this one is a real showstopper for us and as deadlines inch closer, a major stressor...
We tried a workaround suggested on the Lexical Discord (https://github.com/sodenn/lexical-beautiful-mentions/issues/355), but it is a hack (introduces an invisible character) and simply doesn't work reliably.
Any update appreciated!
I've spent more time playing with this and the problem is that Safari simply won't display a caret between a decorator node and a <br>. I did actually manage to fix it by inserting a zero width character before the <br>:
const element = document.createElement('span');
element.innerHTML = '​<br>';
// @ts-expect-error: internal field
dom.__lexicalLineBreak = element;
dom.appendChild(element);
}
Unfortunately this breaks loads of the Playwright tests, but maybe its a pointer for someone who knows more about Lexical's inner workings?
P.S. I also had a go with a CSS solution:
div[data-lexical-editor="true"] *:has(+ br)::after {
content: "\200b";
}
but this doesn't help with the issue.