简体   繁体   中英

Insert text at cursor in a content editable div

I have a contenteditable div where I need to insert text at the caret position,

This can be easily done in IE by document.selection.createRange().text = "banana"

Is there a similar way of implementing this in Firefox/Chrome?

(I know a solution exists here , but it can't be used in contenteditable div, and looks clumsy)

Thank you!

The following function will insert text at the caret position and delete the existing selection. It works in all the mainstream desktop browsers:

function insertTextAtCaret(text) {
    var sel, range;
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();
            range.insertNode( document.createTextNode(text) );
        }
    } else if (document.selection && document.selection.createRange) {
        document.selection.createRange().text = text;
    }
}

UPDATE

Based on comment, here's some code for saving and restoring the selection. Before displaying your context menu, you should store the return value of saveSelection in a variable and then pass that variable into restoreSelection to restore the selection after hiding the context menu and before inserting text.

function saveSelection() {
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            return sel.getRangeAt(0);
        }
    } else if (document.selection && document.selection.createRange) {
        return document.selection.createRange();
    }
    return null;
}

function restoreSelection(range) {
    if (range) {
        if (window.getSelection) {
            sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
        } else if (document.selection && range.select) {
            range.select();
        }
    }
}
  1. Get a Selection Object with window.getSelection() .
  2. Use Selection.getRangeAt(0).insertNode() to add a textnode.
  3. If necessary, move the cursor position behind the added text with Selection.modify() . (Not standardized, but this feature is supported in Firefox, Chrome and Safari)

     function insertTextAtCursor(text) { let selection = window.getSelection(); let range = selection.getRangeAt(0); range.deleteContents(); let node = document.createTextNode(text); range.insertNode(node); for(let position = 0; position != text.length; position++) { selection.modify("move", "right", "character"); }; }

UPD: since ~2020 solution is obsoleted (despite it can work yet)

// <div contenteditable id="myeditable">
// const editable = document.getElementById('myeditable')
// editable.focus()
// document.execCommand('insertHTML', false, '<b>B</b>anana')
document.execCommand('insertText', false, 'banana')

I have used next code to insert icons in chat msg

<div class="chat-msg-text" id="chat_message_text" contenteditable="true"></div>

<script>
var lastCaretPos = 0;
var parentNode;
var range;
var selection;

$(function(){
    $('#chat_message_text').focus();

    $('#chat_message_text').on('keyup mouseup',function (e){
        selection = window.getSelection();
        range = selection.getRangeAt(0);
        parentNode = range.commonAncestorContainer.parentNode;
    });
})

function insertTextAtCursor(text) { 

    if($(parentNode).parents().is('#chat_message_text') || $(parentNode).is('#chat_message_text') )
    {
        var span = document.createElement('span');              
        span.innerHTML=text;

        range.deleteContents();        
        range.insertNode(span);  
        //cursor at the last with this
        range.collapse(false);
        selection.removeAllRanges();
        selection.addRange(range);

    }
    else
    {
        msg_text = $("#chat_message_text").html()
        $("#chat_message_text").html(text+msg_text).focus()                 
    }
}

</script>

在此处输入图像描述

Pasting plain text can be handled with the following code.

const editorEle = document.getElementById('editor');

// Handle the `paste` event
editorEle.addEventListener('paste', function (e) {
    // Prevent the default action
    e.preventDefault();

    // Get the copied text from the clipboard
    const text = e.clipboardData
        ? (e.originalEvent || e).clipboardData.getData('text/plain')
        : // For IE
        window.clipboardData
        ? window.clipboardData.getData('Text')
        : '';

    if (document.queryCommandSupported('insertText')) {
        document.execCommand('insertText', false, text);
    } else {
        // Insert text at the current position of caret
        const range = document.getSelection().getRangeAt(0);
        range.deleteContents();

        const textNode = document.createTextNode(text);
        range.insertNode(textNode);
        range.selectNodeContents(textNode);
        range.collapse(false);

        const selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);
    }
});

If you are working with rich editors (like DraftJs) but have no access to their APIs (eg modifying from an extension), these are the solutions I've found:

  • Dispatching a beforeinput event, this is the recommended way, and most editors support
target.dispatchEvent(new InputEvent("beforeinput", {
    inputType: "insertText",
    data: text,
    bubbles: true,
    cancelable: true
}))
  • Dispatching a paste event
const data = new DataTransfer();
data.setData(
    'text/plain',
    text
);
target.dispatchEvent(new ClipboardEvent("paste", {
    dataType: "text/plain",
    data: text,
    bubbles: true,
    clipboardData: data,
    cancelable: true
}));

This last one uses 2 different methods:


If you want to replace all existing text, you have to select it first

function selectTargetText(target) {
    const selection = window.getSelection();
    const range = document.createRange();
    range.selectNodeContents(target);
    selection.removeAllRanges();
    selection.addRange(range);
}

selectTargetText(target)

// wait for selection before dispatching the `beforeinput` event
document.addEventListener("selectionchange",()=>{
    target.dispatchEvent(new InputEvent("beforeinput", {
        inputType: "insertText",
        data: text,
        bubbles: true,
        cancelable: true
    }))
},{once: true})

just an easier method with jquery:

copy the entire content of the div

var oldhtml=$('#elementID').html();

var tobejoined='<span>hii</span>';

//element with new html would be

$('#elementID').html(oldhtml+tobejoined);

simple!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM