如何获得和设置可编辑内容中的插入符号位置?

时间:2020-10-31 05:33:13

标签: javascript html contenteditable

这个问题有一些答案here,但是有一些问题。

基本上我想执行以下操作:

  1. 获取插入符位置
  2. 设置contenteditable的innerHTML(这会重置插入符号的位置)
  3. 将插入符号位置设置为在步骤1中获得的值。

跨浏览器支持似乎使许多现有的答案变得复杂,但我只需要在现代chrome上使用它即可。它还需要使用html。理想情况下,它看起来应该像这样:

var index = getCaretPosition(contentEditableDiv);
onEdit(contentEditableDiv); // <-- callback function that manipulates the text and sets contentEditableDiv.innerHTML = theManipulatedText
setCaretPosition(contentEditableDiv, index);

我尝试浏览documentation,但这并不简单,我认为这个问题还是需要更精简的答案。

1 个答案:

答案 0 :(得分:0)

这似乎对我有用,但我仅在用例中对其进行了测试。

获取

function getCaretIndex(win, contentEditable) {
    var index = 0;
    var selection = win.getSelection();
    var textNodes = textNodesUnder(contentEditable);

    for(var i = 0; i < textNodes.length; i++) {
        var node = textNodes[i];
        var isSelectedNode = node === selection.focusNode;

        if(isSelectedNode) {
            index += selection.focusOffset;
            break;
        }
        else {
            index += node.textContent.length;
        }
    }

    return index;
}

SET

function setCaretIndex(win, contentEditable, newCaretIndex) {
    var cumulativeIndex = 0;
    var relativeIndex = 0;
    var targetNode = null;

    var textNodes = textNodesUnder(contentEditable);

    for(var i = 0; i < textNodes.length; i++) {
        var node = textNodes[i];
        
        if(newCaretIndex <= cumulativeIndex + node.textContent.length) {
            targetNode = node;
            relativeIndex = newCaretIndex - cumulativeIndex;
            break;
        }

        cumulativeIndex += node.textContent.length;
    }

    var range = win.document.createRange();
    range.setStart(targetNode, relativeIndex);
    range.setEnd(targetNode, relativeIndex);
    range.collapse();

    var sel = win.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
}

必需的帮助

function textNodesUnder(node) { // https://stackoverflow.com/a/10730777/3245937
    var all = [];
    for (node=node.firstChild;node;node=node.nextSibling){
        if (node.nodeType==3) {
            all.push(node);
        }
        else {
            all = all.concat(textNodesUnder(node));
        }
    }
    return all;
}

测试(只需调用此功能)

它循环遍历contenteditable中的文本,设置插入号索引,然后读取它。控制台输出为:(setIndex | getIndex)

function testContentEditable() {
    document.body.innerHTML = "<div contenteditable></div>"
    var ce = document.querySelector("[contenteditable]");
    ce.focus();
    ce.innerHTML = "HELLO <span data-foo='true' style='text-decoration: underline;'><span style='color:red;'>WORLD</span> MY</span> NAME IS BOB";
    
    var i = 0;
    var intv = setInterval(function() {
        if(i == ce.innerText.length) {
            clearInterval(intv);
        }

        setCaretIndex(window, ce, i);

        var currentIndex = getCaretIndex(window, ce);
        console.log(i + " | " + currentIndex);

        i++;
    }, 100);
}






FIDDLE

function getCaretIndex(win, contentEditable) {
    var index = 0;
    var selection = win.getSelection();
    var textNodes = textNodesUnder(contentEditable);

    for(var i = 0; i < textNodes.length; i++) {
        var node = textNodes[i];
        var isSelectedNode = node === selection.focusNode;

        if(isSelectedNode) {
            index += selection.focusOffset;
            break;
        }
        else {
            index += node.textContent.length;
        }
    }

    return index;
}

function setCaretIndex(win, contentEditable, newCaretIndex) {
    var cumulativeIndex = 0;
    var relativeIndex = 0;
    var targetNode = null;

    var textNodes = textNodesUnder(contentEditable);

    for(var i = 0; i < textNodes.length; i++) {
        var node = textNodes[i];
        
        if(newCaretIndex <= cumulativeIndex + node.textContent.length) {
            targetNode = node;
            relativeIndex = newCaretIndex - cumulativeIndex;
            break;
        }

        cumulativeIndex += node.textContent.length;
    }

    var range = win.document.createRange();
    range.setStart(targetNode, relativeIndex);
    range.setEnd(targetNode, relativeIndex);
    range.collapse();

    var sel = win.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
}

function textNodesUnder(node) { // https://stackoverflow.com/a/10730777/3245937
    var all = [];
    for (node=node.firstChild;node;node=node.nextSibling){
        if (node.nodeType==3) {
            all.push(node);
        }
        else {
            all = all.concat(textNodesUnder(node));
        }
    }
    return all;
}

function testContentEditable() {
    document.body.innerHTML = "<div contenteditable></div>"
    var ce = document.querySelector("[contenteditable]");
    ce.focus();
    ce.innerHTML = "HELLO <span data-foo='true' style='text-decoration: underline;'><span style='color:red;'>WORLD</span> MY</span> NAME IS BOB";
    
    var i = 0;
    var intv = setInterval(function() {
        if(i == ce.innerText.length) {
            clearInterval(intv);
        }

        setCaretIndex(window, ce, i);

        var currentIndex = getCaretIndex(window, ce);
        console.log(i + " | " + currentIndex);

        i++;
    }, 100);
}
testContentEditable();