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

后端 未结 10 1656
被撕碎了的回忆
被撕碎了的回忆 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:20

    I refactored @Liam's answer. I put it in a class with static methods, I made its functions receive an element instead of an #id, and some other small tweaks.

    This code is particularly good for fixing the cursor in a rich text box that you might be making with

    . I was stuck on this for several days before arriving at the below code.

    edit: His answer and this answer have a bug involving hitting enter. Since enter doesn't count as a character, the cursor position gets messed up after hitting enter. If I am able to fix the code, I will update my answer.

    edit2: Save yourself a lot of headaches and make sure your

    is display: inline-block. This fixes some bugs related to Chrome putting
    instead of
    when you press enter.

    How To Use

    let richText = document.getElementById('rich-text');
    let offset = Cursor.getCurrentCursorPosition(richText);
    // do stuff to the innerHTML, such as adding/removing  tags
    Cursor.setCurrentCursorPosition(offset, richText);
    richText.focus();
    

    Code

    // Credit to Liam (Stack Overflow)
    // https://stackoverflow.com/a/41034697/3480193
    class Cursor {
        static getCurrentCursorPosition(parentElement) {
            var selection = window.getSelection(),
                charCount = -1,
                node;
            
            if (selection.focusNode) {
                if (Cursor._isChildOf(selection.focusNode, parentElement)) {
                    node = selection.focusNode; 
                    charCount = selection.focusOffset;
                    
                    while (node) {
                        if (node === parentElement) {
                            break;
                        }
    
                        if (node.previousSibling) {
                            node = node.previousSibling;
                            charCount += node.textContent.length;
                        } else {
                            node = node.parentNode;
                            if (node === null) {
                                break;
                            }
                        }
                    }
                }
            }
            
            return charCount;
        }
        
        static setCurrentCursorPosition(chars, element) {
            if (chars >= 0) {
                var selection = window.getSelection();
                
                let range = Cursor._createRange(element, { count: chars });
    
                if (range) {
                    range.collapse(false);
                    selection.removeAllRanges();
                    selection.addRange(range);
                }
            }
        }
        
        static _createRange(node, chars, range) {
            if (!range) {
                range = document.createRange()
                range.selectNode(node);
                range.setStart(node, 0);
            }
    
            if (chars.count === 0) {
                range.setEnd(node, chars.count);
            } else if (node && chars.count >0) {
                if (node.nodeType === Node.TEXT_NODE) {
                    if (node.textContent.length < chars.count) {
                        chars.count -= node.textContent.length;
                    } else {
                        range.setEnd(node, chars.count);
                        chars.count = 0;
                    }
                } else {
                    for (var lp = 0; lp < node.childNodes.length; lp++) {
                        range = Cursor._createRange(node.childNodes[lp], chars, range);
    
                        if (chars.count === 0) {
                        break;
                        }
                    }
                }
            } 
    
            return range;
        }
        
        static _isChildOf(node, parentElement) {
            while (node !== null) {
                if (node === parentElement) {
                    return true;
                }
                node = node.parentNode;
            }
    
            return false;
        }
    }
    

提交回复
热议问题