How to set caret(cursor) position in contenteditable element (div)?

后端 未结 10 1520
被撕碎了的回忆
被撕碎了的回忆 2020-11-22 01:41

I have this simple HTML as an example:

text text text
text text text
text text
10条回答
  •  悲&欢浪女
    2020-11-22 02:37

    I think it's not simple to set caret to some position in contenteditable element. I wrote my own code for this. It bypasses the node tree calcing how many characters left and sets caret in needed element. I didn't test this code much.

    //Set offset in current contenteditable field (for start by default or for with forEnd=true)
    function setCurSelectionOffset(offset, forEnd = false) {
        const sel = window.getSelection();
        if (sel.rangeCount !== 1 || !document.activeElement) return;
    
        const firstRange = sel.getRangeAt(0);
    
        if (offset > 0) {
            bypassChildNodes(document.activeElement, offset);
        }else{
            if (forEnd)
                firstRange.setEnd(document.activeElement, 0);
            else
                firstRange.setStart(document.activeElement, 0);
        }
    
    
    
        //Bypass in depth
        function bypassChildNodes(el, leftOffset) {
            const childNodes = el.childNodes;
    
            for (let i = 0; i < childNodes.length && leftOffset; i++) {
                const childNode = childNodes[i];
    
                if (childNode.nodeType === 3) {
                    const curLen = childNode.textContent.length;
    
                    if (curLen >= leftOffset) {
                        if (forEnd)
                            firstRange.setEnd(childNode, leftOffset);
                        else
                            firstRange.setStart(childNode, leftOffset);
                        return 0;
                    }else{
                        leftOffset -= curLen;
                    }
                }else
                if (childNode.nodeType === 1) {
                    leftOffset = bypassChildNodes(childNode, leftOffset);
                }
            }
    
            return leftOffset;
        }
    }
    

    I also wrote code to get current caret position (didn't test):

    //Get offset in current contenteditable field (start offset by default or end offset with calcEnd=true)
    function getCurSelectionOffset(calcEnd = false) {
        const sel = window.getSelection();
        if (sel.rangeCount !== 1 || !document.activeElement) return 0;
    
        const firstRange     = sel.getRangeAt(0),
              startContainer = calcEnd ? firstRange.endContainer : firstRange.startContainer,
              startOffset    = calcEnd ? firstRange.endOffset    : firstRange.startOffset;
        let needStop = false;
    
        return bypassChildNodes(document.activeElement);
    
    
    
        //Bypass in depth
        function bypassChildNodes(el) {
            const childNodes = el.childNodes;
            let ans = 0;
    
            if (el === startContainer) {
                if (startContainer.nodeType === 3) {
                    ans = startOffset;
                }else
                if (startContainer.nodeType === 1) {
                    for (let i = 0; i < startOffset; i++) {
                        const childNode = childNodes[i];
    
                        ans += childNode.nodeType === 3 ? childNode.textContent.length :
                               childNode.nodeType === 1 ? childNode.innerText.length :
                               0;
                    }
                }
    
                needStop = true;
            }else{
                for (let i = 0; i < childNodes.length && !needStop; i++) {
                    const childNode = childNodes[i];
                    ans += bypassChildNodes(childNode);
                }
            }
    
            return ans;
        }
    }
    

    You also need to be aware of range.startOffset and range.endOffset contain character offset for text nodes (nodeType === 3) and child node offset for element nodes (nodeType === 1). range.startContainer and range.endContainer may refer to any element node of any level in the tree (of course they also can refer to text nodes).

提交回复
热议问题