I'm facing a strange issue:
On a contenteditable
<span>
with predefined content in the html, the text cursor jumps back to a certain point whenever you start typing a new word. The location it jumps to is at the end of the original content.
Example:
<span contenteditable>foo</span>
This is happening on Android Chrome 91.0.4472.120, Android 11, using Gboard 10.6.02.371892758-release-arm64-v8a. I also tested on desktop Chrome and IOS Safari. The problem can't be reproduced there, only Andriod Chrome.
This doesn't happen for block elements such as <p>
or <div>
, but if I set display:inline
on say, a <p>
using css, the problem is the same as with the span.
It also doesn't happen if the element does not initially contain text (ex. <span contenteditable></span>
), even if it is edited by the user to contain text afterwards.
Any ideas as to why this is happening? It seems like some sort of bug with Chrome/Android. Is there a workaround/fix for it?
Note: I need to use contenteditable and inline since I want the element to wrap without taking up the whole line, so input or textarea isn't an option.
Here's a workaround that I found.
If text is added to an empty element using Range.insertNode()
while the element is selected/focused, it won't trigger the issue.
As such, we can clear the content, select the element, then re-insert the original content using this method when the element is focused to prevent the issue.
const onFocus = (e) => {
const selection = window.getSelection();
const selectionRange = new Range();
const originalContent = e.target.textContent;
// Clear the content and select in the empty element
e.target.textContent = '';
selectionRange.selectNodeContents(e.target);
selection.removeAllRanges();
selection.addRange(selectionRange);
// Re-insert the content and set the cursor at the end
const textNode = document.createTextNode(originalContent);
selectionRange.insertNode(textNode);
selectionRange.selectNodeContents(textNode);
selectionRange.collapse(false);
}
const element = document.getElementById('element');
element.addEventListener('focus', onFocus, {once : true});
<span id="element" contenteditable>foo</span>
Side effect: I couldn't manage to reset the caret to its initial position after the re-insertion, so instead I place the cursor at the end of the content. This means that tapping on the middle of the text will go to the end instead, but the user can always tap again after the element is focused.