在包含 HTML 内容的 contentEditable 区域中获取插入符号(光标)位置

IT技术 javascript wysiwyg contenteditable getselection
2021-01-17 22:56:17

我有 contentEditable 元素(可以是 p、div、...),我想在其中获得插入符号(光标)位置。我通常可以用这段代码实现它:

var position = window.getSelection().getRangeAt(0).startOffset;

当元素仅包含文本时,这可以正常工作。但是当元素包含一些 HTML 格式时,返回的位置是相对于包含的 HTML 元素中插入符号的位置。

让我们假设 contentEditable 元素的内容是这样的:

AB<b>CD</b>EF

如果 caret 在 inside <b></b>,比方说在 C 和 D 之间,上面代码的返回位置是 1 而不是 3(从 contentEditable 元素的内容开始计算)

任何人都可以提出解决方案吗?

3个回答

更新

我写了一个更简单的版本,它也适用于 IE < 9:

https://stackoverflow.com/a/4812022/96100

旧答案

这实际上是一个比整个文档文本中的字符偏移更有用的结果:startOffsetDOM 范围属性(window.getSelection().getRangeAt()返回的内容)是相对于其startContainer属性的偏移(不一定总是文本节点,通过道路)。然而,如果你真的想要一个字符偏移,这里有一个函数可以做到。

这是一个活生生的例子:http : //jsfiddle.net/timdown/2YcaX/

这是函数:

function getCharacterOffsetWithin(range, node) {
    var treeWalker = document.createTreeWalker(
        node,
        NodeFilter.SHOW_TEXT,
        function(node) {
            var nodeRange = document.createRange();
            nodeRange.selectNode(node);
            return nodeRange.compareBoundaryPoints(Range.END_TO_END, range) < 1 ?
                NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
        },
        false
    );

    var charCount = 0;
    while (treeWalker.nextNode()) {
        charCount += treeWalker.currentNode.length;
    }
    if (range.startContainer.nodeType == 3) {
        charCount += range.startOffset;
    }
    return charCount;
}
@Frodik:完成。参见jsfiddle.net/timdown/2YcaX/3我也更新了答案。
2021-03-14 22:56:17
是的,我知道这一点。我的 webapp 不支持 IE <= 8,因为它大量使用 HTML5 功能。所以不用担心。
2021-03-19 22:56:17
感谢您的回答,看起来不错,但是它有一个小错误。你能不能尝试修复它,因为我已经尝试过但没有运气。我已将您的代码更改为内联显示插入符号位置,并且它还会对 keyup 事件做出反应。该代码运行良好,但当插入符号位于标记的开头或结尾时,它返回错误的结果。自己尝试一下,只需单击 contentEditable div 并按住向右箭头键即可移动插入符号。 jsfiddle.net/2YcaX/1
2021-03-22 22:56:17
@Frodik:因为我没有在其他任何地方提到这一点,所以我会在这里提到:这个答案不适用于 IE <= 8。我没有尝试在这里为 IE 提供解决方案,因为您的原始问题正在使用window.getSelection(), IE 不支持。
2021-03-22 22:56:17
非常感谢蒂姆,现在它完美无瑕,正是我所需要的。再次感谢你。
2021-03-25 22:56:17

这是一篇很老的帖子,但仍然是在谷歌上搜索的第一个结果之一,所以也许仍然有用。考虑到 html 标签和换行符,这对我来说也是正确的(在 Firefox 上测试):

function getCaretPosition (node) {
    var range = window.getSelection().getRangeAt(0),
        preCaretRange = range.cloneRange(),
        caretPosition,
        tmp = document.createElement("div");

    preCaretRange.selectNodeContents(node);
    preCaretRange.setEnd(range.endContainer, range.endOffset);
    tmp.appendChild(preCaretRange.cloneContents());
    caretPosition = tmp.innerHTML.length;
    return caretPosition;
}

它使用 cloneContents 功能来获取实际的 html 并将文档片段附加到临时 div 以获取 html 长度。

对于包含格式化 HTML 的 contentEditable div,这对我来说比当前接受的答案(作者的原始版本和更新版本)要好得多。
2021-03-20 22:56:17

如果要插入元素,则可以尝试执行以下操作:

// Get range
var range = document.caretRangeFromPoint(event.clientX, event.clientY);
if (range)
  range.insertNode(elementWhichYouWantToAddToContentEditable);
带有 webkit 的 document.caretRangeFromPoint(x,y) 和带有 firefox 的 document.caretPositionFromPoint(x,y)? developer.mozilla.org/en-US/docs/Web/API/Document/...知道做这个工作:gist.github.com/unicornist/ac997a15bc3211ba1235
2021-04-03 22:56:17
看起来caretRangeFromPoint是特定于 Firefox 的;真可惜。
2021-04-07 22:56:17