23

I am using a contenteditable div, when I tried to paste something with style, it was suppose to only copy the plain text, but it got the style as well, does anyone know how to force it to convert to plain text when I paste it or anyone has a better solution

Here is my code:

    <div contenteditable="true">   
      This text can be edited by the user. 
    </div>

enter image description here enter image description here

FlyingBurger
  • 1,182
  • 2
  • 16
  • 21
  • 3
    Why wouldn't you just use an `` or ` – n8jadams Nov 21 '19 at 17:13
  • Indeed...unable to reproduce. Are you sure you pasted *text* it looks like you pasted a whole bunch of HTML – Paulie_D Nov 21 '19 at 17:20
  • 1
    @n8jadams Because I want it can be auto resized based on the length of content, I don’t want to use scroll bar. Plus, I need to vertically center the placeholder and text. Have tried both ways, maybe contentditable will be easier. – FlyingBurger Nov 21 '19 at 17:29
  • 1
    @Paulie_D you clearly did not paste rich text if you could not reproduce. When you copy rich text or html and paste it into a contenteditable, the styles and elements are added. – epascarello Nov 21 '19 at 18:36

6 Answers6

37

When you paste in rich content, it will be displayed as rich content. So you would need to capture the paste event, prevent the default action, and read the text from the clipboard.

var ce = document.querySelector('[contenteditable]')
ce.addEventListener('paste', function (e) {
  e.preventDefault()
  var text = e.clipboardData.getData('text/plain')
  document.execCommand('insertText', false, text)
})
  [contenteditable] {
    background-color: black;
    color: white;
    width: 400px;
    height: 200px;
  }
<div contenteditable="true"></div>

<div>
  <h1>Test content</h1>
  <p style="color:red">Copy <em>this</em> <u>underlined</u></p>
</div>
epascarello
  • 204,599
  • 20
  • 195
  • 236
  • 1
    I think it should be `insertHTML` instead of `insertText`, since the later will convert new-lines into divs. – Lafi Jun 15 '21 at 01:10
  • document.execCommand is deprecated. Is it okay to use it? – T H May 15 '22 at 20:42
  • @TàiHatranduc I think you are answering your own question as you ask it :) – Sergey Jun 03 '22 at 22:27
  • My question is why "execCommand" is being used in these examples, why not just grab that text and set the text value to be what was in the clipboard (after stripping out formatting)... or am I missing something obvious? – Sergey Jun 03 '22 at 22:28
  • 1
    @Sergey Because the text may not be the entire innerHTML of the element. They may be pasting content between two words. They may have selected a paragraph and are pasting to replace it. This takes care out that. You can do it without the command, but it is a lot of code. The Other answer just replaces the content, but you can see the comment says why it does not work correctly – epascarello Jun 04 '22 at 03:14
2

You can intercept the "paste" event and replace the content of the target.

/* Derived from: https://stackoverflow.com/a/6035265/1762224 */
const onPastePlainText = (e) => {
  var pastedText = undefined;
  if (window.clipboardData && window.clipboardData.getData) { // IE
    pastedText = window.clipboardData.getData('Text');
  } else if (e.clipboardData && e.clipboardData.getData) {
    pastedText = e.clipboardData.getData('text/plain');
  }
  e.target.textContent = pastedText;
  e.preventDefault();
  return false;
}

document.querySelector('.ediatable-div').addEventListener('paste', onPastePlainText);
.ediatable-div {
  border: 2px inset #EEE;
  height: 25vh;
}

/* Placeholder - Derived from: https://stackoverflow.com/a/20300212/1762224 */
[contentEditable=true]:empty:not(:focus):before {
  content: attr(data-text);
  color: #AAA;
}
<div class="ediatable-div" contenteditable="true" data-text="Paste copied HTML here"></div>
<div>
  <p style="text-decoration:underline">Copy <strong>me</strong>, I have <em>style</em>!</p>
</div>
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • 3
    This doesn't work where there is already some text in the field. – Santosh Mar 09 '21 at 04:53
  • Ah yes, this essentially overwrites ALL contents and replaces them with what was in the clipboard, but this needs to append to the text (including into the exact spot where the cursor is, or even more challenging [in case partial text was _selected_]) – Sergey Jun 03 '22 at 22:31
  • Same problem as obove: The problem ist you can not use `undo`... (CTRL+Z) – Mqx Feb 25 '23 at 18:52
2

The following solution works when there is already text in the field that is being pasted into, and it does not use the deprecated document.execCommand() function.

// Edited from https://htmldom.dev/paste-as-plain-text/

const editable_div = document.getElementById('editable_div');

// Handle the paste event
editable_div.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')
        : '';

    // 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);
});
<div id="editable_div" contenteditable="true">
    You can only paste plain text into me!
</div>
2

There is a non-standard user-modify CSS property which is supported by WebKit-based browsers:

-webkit-user-modify: read-write-plaintext-only;

This eliminates the need to use deprecated execCommand API or error-prone steps to position caret correctly.

[contenteditable] {
  -webkit-user-modify: read-write-plaintext-only;
}
<div><p style="color:red">Copy <em>this</em> <u>styled</u> text</p></div>
<div contenteditable="true">into this editable element</div>

There is one strange behavior: copy-and-paste text selected via double-click may introduce extra leading and/or trailing whitespaces (on macOS at least).


To support non-WebKit browsers, you still need to listen to paste event, such as:

someEditableElement.addEventListener("paste", function (e) {
  e.preventDefault();
  const text = e.clipboardData ? e.clipboardData.getData("text/plain") : "";

  if (document.queryCommandSupported?.("insertText")) {
    return document.execCommand("insertText", false, text);
  }

  const selection = document.getSelection();
  if (!selection) return;
  const range = selection.getRangeAt(0);
  range.deleteContents();
  range.insertNode(new Text(text));
  range.collapse(); // select nothing
  selection.removeAllRanges(); // position caret after inserted text
  selection.addRange(range); // show caret
});

There are differences between these two approaches. Use your discretion.

Baoshan Sheng
  • 2,635
  • 1
  • 11
  • 11
  • Thanks! Second approach works fine in React (NextJS). I have juste removed the execComand block! Just be carefull with "Text" if you have a component with same name! – Duda Gervásio May 10 '23 at 21:36
2

You can set the contenteditable attribute value to plaintext-only like this:

<div contentEditable="plaintext-only"></div>

indicates that the element's raw text is editable, but rich text formatting is disabled

Well supported most browsers but Firefox

KyleMit
  • 30,350
  • 66
  • 462
  • 664
septlog
  • 421
  • 2
  • 3
0

When contenteditable elements might be dynamically added to the DOM, it is useful to have a document-level handler that, once there, will detect future instances as well as existing ones.

// 1. Paste plain text only into contenteditable elements
document.addEventListener("paste", function (e) {
    if (e.target.isContentEditable) {
        e.preventDefault();
        var text = e.clipboardData.getData('text/plain')
        document.execCommand('insertText', false, text)
    }
});

// 2. Prevent any paste into contenteditable elements
document.addEventListener("paste", function (e) {
    if (e.target.isContentEditable) {
        e.preventDefault();
        return false;
    }
});
DeeGee
  • 160
  • 6