Replace specific word in contenteditable

前端 未结 3 1734
我寻月下人不归
我寻月下人不归 2021-02-08 07:52

I have a contenteditable div

I need to get the last word from caret position and on certai

3条回答
  •  旧时难觅i
    2021-02-08 08:48

    To work with Ranges we need to keep in mind that we are working with Nodes, not only the text that is rendered. The structure you want to manipulate is:

    <-- Element Node "some text" <-- TextNode

    But it also could be:

    <-- Element Node "some text" <-- TextNode "more text" <-- TextNode "" <-- TextNode

    To solve your problem is simplier to handle only one TextNode, I propose to use the normalize() function to join all of them into a single one.

    Then you only need to set the Range to the word's bounds before deleteContents(). Once deleted, you can insert a new TextNode with the substitution using insertNode().

    var wordStart = range.toString().lastIndexOf(lastWord);
    var wordEnd = wordStart + lastWord.length;
    
    /* containerEl.firstChild refers to the div's TextNode */                   
    range.setStart(containerEl.firstChild, wordStart);
    range.setEnd(containerEl.firstChild, wordEnd);
    range.deleteContents();
    range.insertNode(document.createTextNode(resultValue));
    

    For this to work, you need that the text is in a single TextNode. But after ìnsertNode the div will contain multiple text nodes. To fix this simply call normalize() to join all TextNode elements.

    containerEl.normalize();
    

    Edit:

    As Basj points out, the original solution fails for multiline. That's because when hitting ENTER the structure changes from:

    <-- Element Node "some text" <-- TextNode

    to something like:

    <-- Element Node
    "some text"
    "more text"

    I've updated this answer, but it's also worth to read Basj's answer at this question: Replace word before cursor, when multiple lines in contenteditable

    JSFiddle demo or runnable code snippet:

    document.getElementById('divTest').onkeyup = function (e) {
        if (e.keyCode == 32) {
            getWordPrecedingCaret(this);
        }
    };
    
    function getWordPrecedingCaret(containerEl) {
        var preceding = "",
            sel,
            range,
            precedingRange;
        if (window.getSelection) {
            sel = window.getSelection();
            if (sel.rangeCount > 0) {
                range = sel.getRangeAt(0).cloneRange();
                range.collapse(true);
                range.setStart(containerEl, 0);
                preceding = range.toString();
            }
        } else if ((sel = document.selection) && sel.type != "Control") {
            range = sel.createRange();
            precedingRange = range.duplicate();
            precedingRange.moveToElementText(containerEl);
            precedingRange.setEndPoint("EndToStart", range);
            preceding = precedingRange.text;
        }
    
        var words = range.toString().trim().split(' '),
            lastWord = words[words.length - 1];
            
        if (lastWord) {
            var resultValue = 'some'; // this value is coming from some other function
            if (resultValue == lastWord) {
                console.log('do nothing: ' + lastWord);
                // do nothing
            } else {
                console.log('replace word ' + lastWord);
                
                /* Find word start and end */
                var wordStart = range.endContainer.data.lastIndexOf(lastWord);
                var wordEnd = wordStart + lastWord.length;
                console.log("pos: (" + wordStart + ", " + wordEnd + ")");
                               
                range.setStart(range.endContainer, wordStart);
                range.setEnd(range.endContainer, wordEnd);
                range.deleteContents();
                range.insertNode(document.createTextNode(resultValue));
                // delete That specific word and replace if with resultValue
    
                /* Merge multiple text nodes */            
                containerEl.normalize();
            }
            return lastWord;
        }
    }
    Write words here and hit SPACE BAR

提交回复
热议问题