Javascript trick for 'paste as plain text` in execCommand

Viewed 116927

I have a basic editor based on execCommand following the sample introduced here. There are three ways to paste text within the execCommand area:

  • Ctrl+V
  • Right Click -> Paste
  • Right Click -> Paste As Plain Text

I want to allow pasting only plain text without any HTML markup. How can I force the first two actions to paste Plain Text?

Possible Solution: The way I can think of is to set listener for keyup events for (Ctrl+V) and strip HTML tags before paste.

  1. Is it the best solution?
  2. Is it bulletproof to avoid any HTML markup in paste?
  3. How to add listener to Right Click -> Paste?
13 Answers

None of the posted answers really seems to work cross browser or the solution is over complicated:

  • The command insertText is not supported by IE
  • Using the paste command results in stack overflow error in IE11

What worked for me (IE11, Edge, Chrome and FF) is the following:

$("div[contenteditable=true]").off('paste').on('paste', function(e) {
    e.preventDefault();
    var text = e.originalEvent.clipboardData ? e.originalEvent.clipboardData.getData('text/plain') : window.clipboardData.getData('Text');
    _insertText(text);
});

function _insertText(text) { 
    // use insertText command if supported
    if (document.queryCommandSupported('insertText')) {
        document.execCommand('insertText', false, text);
    }
    // or insert the text content at the caret's current position
    // replacing eventually selected content
    else {
        var range = document.getSelection().getRangeAt(0);
        range.deleteContents();
        var textNode = document.createTextNode(text);
        range.insertNode(textNode);
        range.selectNodeContents(textNode);
        range.collapse(false);

        var selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);
    }
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<textarea name="t1"></textarea>
<div style="border: 1px solid;" contenteditable="true">Edit me!</div>
<input />
</body>

Note that the custom paste handler is only needed/working for contenteditable nodes. As both textarea and plain input fields don't support pasting HTML content at all, so nothing needs to be done here.

Note that execCommand() is deprecated, so avoid using it in your production environments. Do something like this instead:

editor.addEventListener('paste', handlePaste);

function handlePaste(e) {
  e.preventDefault();

  const text = (e.clipboardData || window.clipboardData).getData('text');
  const selection = window.getSelection();

  if (!selection.rangeCount) {
    return false;
  }

  selection.deleteFromDocument();
  selection.getRangeAt(0).insertNode(document.createTextNode(text));
}

References:

After along search and trying I have found somehow kind of optimal solution

what is important to keep in mind

// /\x0D/g return key ASCII
window.document.execCommand('insertHTML', false, text.replace('/\x0D/g', "\\n"))


and give the css style white-space: pre-line //for displaying

var contenteditable = document.querySelector('[contenteditable]')
            contenteditable.addEventListener('paste', function(e){
                let text = ''
                contenteditable.classList.remove('empty')                
                e.preventDefault()
                text = (e.originalEvent || e).clipboardData.getData('text/plain')
                e.clipboardData.setData('text/plain', '')                 
                window.document.execCommand('insertHTML', false, text.replace('/\x0D/g', "\\n"))// /\x0D/g return ASCII
        })
#input{
  width: 100%;
  height: 100px;
  border: 1px solid black;
  white-space: pre-line; 
}
<div id="input"contenteditable="true">
        <p>
        </p>
</div>   

OK as everybody is trying to work around clipboard data, checking keypress event, and using execCommand.

I thought of this

CODE

handlePastEvent=()=>{
    document.querySelector("#new-task-content-1").addEventListener("paste",function(e)
    {
        
        setTimeout(function(){
            document.querySelector("#new-task-content-1").innerHTML=document.querySelector("#new-task-content-1").innerText.trim();
        },1);
    });

}
handlePastEvent();
<div contenteditable="true" id="new-task-content-1">You cann't paste HTML here</div>

In 2022, you can use CSS user-modify: read-write-plaintext-only to archive this

.plain-text-only {
  user-modify: read-write-plaintext-only;
  -moz-user-modify: read-write-plaintext-only;
  -webkit-user-modify: read-write-plaintext-only;
}

div[contenteditable] {
  padding: 1rem 0.5rem;
  border: 2px solid #eee;
  border-radius: 4px;
  margin-bottom: 2rem;
}
<div contenteditable class="plain-text-only">Can't paste HTML here</div>
<div contenteditable>HTML styled text free</div>
Related