javascript - 如何获取 ContentEditable 元素中相对于 innerText 的插入符号位置?
问题描述
我知道有很多关于在ContentEditable
元素中获取插入符号位置的现有问题。几乎所有这些现有的解决方案都提供了相对于textContent
. 一些例子是this one或this one。
我们目前正在开发两个 WebExtensions 来根据用户类型进行自动更正。例如,如果用户键入:)
,它可以将其自动更正为 。为了使自动更正起作用,它需要获得相对于innerText
. textContent
可以包含空白字符和其他在呈现文本时实际上不会出现的差异,这会破坏自动更正功能。
我们当前的方法部分改编自这个答案:https ://stackoverflow.com/a/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 不喜欢 null 字符,但我们尝试使用其他非打印字符并遇到同样的问题。
我们正在寻找另一种确定插入符号位置的方法,该方法innerText
适用于所有网站,包括 Twitter。它需要支持最新版本的 Firefox 和 Chrome,包括 Firefox ESR。它还需要高性能,因为它在每次按键时都会运行。
交叉发布在Mozilla 的 Discourse上。
解决方案
脚步
我从元素中得到Selection
andRange
target
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>
推荐阅读
- jenkins-pipeline - 将 salt minion id 从平面文件传递给 Jenkins 管道
- python - 当使用python使用下拉菜单时,如何在excel中更改整个行的颜色
- excel - 根据标题值复制/粘贴单元格
- python - 将实时条形图与折线图一起绘制
- rest - Coldfusion 11 - 添加 Rest API 服务主机问题
- postgresql - 更改列会锁定 postgresql 中的表吗?
- oracle - 在 oracle 钱包中指定证书和密钥
- android - 将两个视图放在屏幕中央
- android - 如何在 Android kotlin 中创建多级回收视图?
- azure - Cannot connect to Azure Redis after changing Minimum TLS version to 1.2