如何在 Android Chrome 上修复内联 contenteditable 中的光标跳转?

问题描述

我遇到了一个奇怪的问题:

在 html 中具有预定义内容contenteditable <span> 上,每当您开始输入新单词时,文本光标都会跳回到某个点。它跳转到的位置在原始内容的末尾。

示例:

<span contenteditable>foo</span>

这发生在使用 Gboard 10.6.02.371892758-release-arm64-v8a 的 Android Chrome 91.0.4472.120、Android 11 上。 我还在桌面 Chrome 和 IOS Safari 上进行了测试。问题在那里无法重现,只有Andriod Chrome。

这不会发生在 <p><div> 之类的块元素上,但是如果我设置 display:inline 上说,一个使用 css 的 <p>,问题是与跨度相同。

如果元素最初不包含文本(例如 <span contenteditable></span>),即使用户编辑它之后包含文本,也不会发生这种情况。

关于为什么会发生这种情况的任何想法? Chrome/Android 似乎存在某种错误。是否有解决方法/修复方法

注意:我需要使用 contenteditable 和 inline,因为我希望元素在不占用整行的情况下进行换行,因此 input 或 textarea 不是一个选项。

解决方法

这是我找到的解决方法。

如果在选择/聚焦元素时使用 Range.insertNode() 将文本添加到空元素,则不会触发该问题。

因此,我们可以清除内容,选择元素,然后在元素聚焦时使用此方法重新插入原始内容以防止出现问题。

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>

副作用:重新插入后我无法将插入符号重置为其初始位置,因此我将光标放在内容的末尾。这意味着点击文本中间会转到最后,但用户始终可以在元素获得焦点后再次点击。