32

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

4
  • 3
    Why wouldn't you just use an <input> or <textarea> for this? The contenteditable="true" on a non-traditional input is terrible for accessibility and its cross-browser support isn't that great either. Inputs and TextAreas are plenty customizable. Commented Nov 21, 2019 at 17:13
  • Indeed...unable to reproduce. Are you sure you pasted text it looks like you pasted a whole bunch of HTML Commented Nov 21, 2019 at 17:20
  • 2
    @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. Commented Nov 21, 2019 at 17:29
  • 2
    @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. Commented Nov 21, 2019 at 18:36

7 Answers 7

49

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>

Sign up to request clarification or add additional context in comments.

5 Comments

I think it should be insertHTML instead of insertText, since the later will convert new-lines into divs.
document.execCommand is deprecated. Is it okay to use it?
@TàiHatranduc I think you are answering your own question as you ask it :)
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 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
26
+400

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, with Firefox adding support in v136

3 Comments

It works perfectly without any JS. Best answer! Thanks!
At the time of writing this comment, it does not work in Firefox
This makes pasting img not possible. If you just want both plain text and images could be pasted, don't use it.
6

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.

1 Comment

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!
3

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>

3 Comments

This doesn't work where there is already some text in the field.
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])
Same problem as obove: The problem ist you can not use undo... (CTRL+Z)
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>

1 Comment

The problem ist you can not use undo... (CTRL+Z)
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;
    }
});

Comments

0

A HTML-only solution with no coding required is using the "plaintext-only" type for contentenditable elements:

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

See https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/contentEditable

1 Comment

This is the ideal solution, but it has already been posted here a couple of years ago: stackoverflow.com/a/76881059/1650337

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.