如何获取ContentEditable元素相对于innerText的插入符位置?

3
我知道有很多现有的问题与获取 ContentEditable 元素中插入符位置有关。几乎所有这些现有解决方案都提供了相对于 textContent 的插入符位置。一些例子是 this onethis one
我们目前正在开发两个 WebExtensions 来进行自动更正,以便在用户输入时进行更正。例如,如果用户键入 :),它可以自动更正为“。”为了使自动更正起作用,它需要相对于 innerText 获取插入符位置。textContent 可包括空格字符和其他差异,这些差异在文本呈现时实际上并不出现,这会破坏自动更正功能。
我们目前的方法部分源于这个答案:https://dev59.com/oonca4cB1Zd3GeqP5xEM#29258657,它提供了相对于innerHTML的插入符位置。它克隆元素,插入空字符,确定索引,然后移除空字符。
// document.designMode is handled the same, see  https://github.com/rugk/unicodify/issues/54
if (target.isContentEditable || document.designMode === "on") { 
     target.focus(); 
     const _range = document.getSelection().getRangeAt(0); 
     if (!_range.collapsed) { 
         return null; 
     } 
     const range = _range.cloneRange(); 
     const temp = document.createTextNode("\0"); 
     range.insertNode(temp); 
     const caretposition = target.innerText.indexOf("\0"); 
     temp.parentNode.removeChild(temp); 
     return caretposition; 
}

请查看我们的原始源代码以获取完整上下文。这种方法似乎在99%的网站上都可以正常工作,但在Twitter上会出现问题。当用户输入时,光标会不断重置到行首,导致文本混乱(有关更多信息,请参见相应问题)。我们猜测Twitter不喜欢空字符,但我们尝试了其他非打印字符,也遇到了同样的问题。
我们正在寻找另一种方法来确定插入符号相对于innerText的位置,这种方法将适用于所有网站,包括Twitter。它需要支持最新版本的Firefox和Chrome,包括Firefox ESR。它还需要具有良好的性能,因为它在每次按键时都会运行。

转载自Mozilla的Discourse论坛

1个回答

0

步骤


我从目标元素获取选择范围

const selection = document.getSelection();
const range = document.createRange();

在检查 Range 是否折叠后,您的基本代码将插入 null 字符并获取插入符位置

const temp = document.createTextNode("\0");
selection.getRangeAt(0).insertNode(temp);
caretPosition = target.innerText.indexOf("\0");
temp.parentNode.removeChild(temp);

我使用这个代码片段来设置光标的位置,这应该是解决Twitter问题的方法。
range.setStart(selection.focusNode, selection.focusOffset);
range.collapse(false);

selection.removeAllRanges();
selection.addRange(range);

const target = document.getElementById('text');

target.addEventListener('keyup', () => {
  let caretPosition = null;
  const selection = document.getSelection();
  const range = document.createRange();
  
  if (range.collapsed) {
    const temp = document.createTextNode("\0");
    selection.getRangeAt(0).insertNode(temp);
    caretPosition = target.innerText.indexOf("\0");
    temp.parentNode.removeChild(temp);

    range.setStart(selection.focusNode, selection.focusOffset);
    range.collapse(false);

    selection.removeAllRanges();
    selection.addRange(range);
  }
  
  console.log(JSON.stringify({ caretPosition }));
});
#text {
  border: 1px solid;
  padding: 1rem;
  width: 50vw;
  height: 50vh;
}
<h3>ContentEditable Div</h3>
<div id="text" contenteditable="true">This text can be edited by the user.<br> Some <strong>bold</strong> and <em>italic and <strong>bold</strong></em> text.</div>


我们已经尝试按照这样的方式进行实现,但是它失败了,也就是说它不起作用。当它与ContentEditable Div一起使用时,每次按键都会出现错误,因为插入符位置是错误的。在此处进行了测试,使用contenteditable。 - rugk
@rugk。已编辑并修复错误。如果没问题,请告诉我。 - a.mola
这是另一个JSFiddle来重现错误:https://jsfiddle.net/tcomh4zb/,现在也已经根据您的编辑进行了调整:https://jsfiddle.net/3mskc146/。除了JsFiddle在您的代码中显示一些linting错误之外,它似乎现在还无法处理换行符。@a.mola - rugk
好的,我该如何使用test branch/Pull Request来测试它在Twitter上的工作方式呢?@rugk - a.mola
@rugk; 我还没有找到解决这个问题的方法;有什么运气吗? - a.mola
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接