javascript:带有html标签的focusOffset

use*_*521 11 html javascript php jquery

我有一个contenteditable div如下(| =光标位置):

<div id="mydiv" contenteditable="true">lorem ipsum <spanclass="highlight">indol|or sit</span> amet consectetur <span class='tag'>adipiscing</span> elit</div>
Run Code Online (Sandbox Code Playgroud)

我想得到当前的光标位置,包括html标签.我的代码:

var offset = document.getSelection().focusOffset;
Run Code Online (Sandbox Code Playgroud)

偏移量返回5(最后一个标签的全文)但我需要它来处理html标签.预期的返回值是40.代码必须与所有最近的浏览器一起使用.(我也检查了这个:带有HTML标签的window.getSelection()偏移量?但它没有回答我的问题).有任何想法吗 ?

Alv*_*oro 10

编辑:这是一个旧的答案,不适用于OP要求具有相同文本的节点.但如果你没有这个要求,它会更清洁,更轻巧.

这是您可以使用的一个选项,适用于所有主流浏览器:

  1. 获取插入符号在其节点内的偏移量(document.getSelection().anchorOffset)
  2. 获取插入符所在节点的文本(document.getSelection().anchorNode.data)
  3. #mydiv通过使用获取该文本的偏移量indexOf()
  4. 添加在1和3中获得的值,以获得div中插入符号的偏移量.

对于您的特定情况,代码看起来像这样:

var offset = document.getSelection().anchorOffset;
var text = document.getSelection().anchorNode.data;
var textOffset = $("#mydiv").html().indexOf( text );

offsetCaret = textOffset + offset;
Run Code Online (Sandbox Code Playgroud)

您可以在此JSFiddle上查看工作演示(查看控制台以查看结果).

一个更通用的函数版本(允许传递div作为参数,因此它可以与不同的一起使用contenteditable)在另一个JSFiddle上:

function getCaretHTMLOffset(obj) {

    var offset = document.getSelection().anchorOffset;
    var text = document.getSelection().anchorNode.data;
    var textOffset = obj.innerHTML.indexOf( text );

    return textOffset + offset;

}
Run Code Online (Sandbox Code Playgroud)

关于这个答案

  • 它可以在所有最近的浏览器中按要求工作(在Chrome 42,Firefox 37和Explorer 11上测试).
  • 它简短明了,不需要任何外部库(甚至不是jQuery)
  • 问题:如果您有不同的节点具有相同的文本,它可能会返回第一次出现的偏移而不是插入符的实际位置.


Lou*_*uis 9

另一种方法是在DOM中添加一个临时标记并计算该标记的偏移量.该算法在感兴趣outerHTML的内部序列化(the innerHTML)中查找标记(其)的HTML序列化div.重复文本不是此解决方案的问题.

为此,标记的序列化在其div中必须是唯一的.您无法控制用户键入字段的内容,但您可以控制放入DOM的内容,因此这不难实现.在我的示例中,标记是静态唯一的:通过选择不太可能导致冲突的类名.通过检查DOM并更改类直到它是唯一的,它也可以动态地完成.

我有一个小提琴(源自Alvaro Montoro自己的小提琴).主要部分是:

function getOffset() {

    if ($("." + unique).length)
        throw new Error("marker present in document; or the unique class is not unique");

    // We could also use rangy.getSelection() but there's no reason here to do this.
    var sel = document.getSelection();

    if (!sel.rangeCount)
        return; // No ranges.

    if (!sel.isCollapsed)
        return; // We work only with collapsed selections.

    if (sel.rangeCount > 1)
        throw new Error("can't handle multiple ranges");

    var range = sel.getRangeAt(0);
    var saved = rangy.serializeSelection();
    // See comment below.
    $mydiv[0].normalize();

    range.insertNode($marker[0]);
    var offset = $mydiv.html().indexOf($marker[0].outerHTML);
    $marker.remove();

    // Normalizing before and after ensures that the DOM is in the same shape before 
    // and after the insertion and removal of the marker.
    $mydiv[0].normalize();
    rangy.deserializeSelection(saved);

    return offset;
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,代码必须补偿将标记添加到DOM中并将其删除,因为这会导致当前选择丢失:

  1. Rangy用于保存选择并在之后恢复.请注意,保存和恢复可以使用比Rangy更轻的东西来完成,但我不想用minutia加载答案.如果您决定使用Rangy执行此任务,请阅读文档,因为可以优化序列化和反序列化.

  2. 为了让Rangy工作,DOM必须在保存之前和之后处于完全相同的状态.这就是normalize()我们在添加标记之前和删除标记之后调用的原因.这样做是将紧邻的文本节点合并为单个文本节点.问题是向DOM添加标记可能会导致文本节点被分成两个新的文本节点.这会导致选择丢失,如果没有通过规范化撤消,将导致Rangy无法恢复选择.再一次,比调用更轻松的东西normalize可以做到这一点,但我不想用minutia加载答案.