【问题标题】:How do you get and set the caret position in a contenteditable?您如何在 contenteditable 中获取和设置插入符号位置?
【发布时间】:2020-10-31 05:33:13
【问题描述】:

这个问题有一些答案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,但这并不简单,而且我认为这个问题无论如何都应该得到更精简的答案。

【问题讨论】:

标签: javascript html contenteditable


【解决方案1】:

这似乎对我有用,但我只针对我的用例进行了测试。

获取

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;
}

TEST(只要调用这个函数)

它遍历 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);
}






小提琴

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();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-24
    • 2016-07-12
    • 2020-10-29
    • 2018-05-31
    • 2014-07-29
    • 2018-05-06
    • 1970-01-01
    • 2012-05-11
    相关资源
    最近更新 更多