41

I have a contentEditable Div and I want remove any formatting especially for copy and paste text.

Garth Humphreys
  • 793
  • 2
  • 9
  • 16
  • possible duplicate of [Sanitize/Rewrite HTML on the Client Side](http://stackoverflow.com/questions/295566/sanitize-rewrite-html-on-the-client-side) – Dave Jarvis Sep 30 '13 at 03:59
  • You can use **removeFormat** with the **execCommand** method [https://developer.mozilla.org/es/docs/Web/API/Document/execCommand](https://developer.mozilla.org/es/docs/Web/API/Document/execCommand) – Federico Rocha Mar 13 '20 at 18:22

9 Answers9

57

You can add a listener to the "paste" event and reformat the clipboard contents. Like so:

let editableDiv = document.querySelector('div[contenteditable="true"]');

editableDiv.addEventListener("paste", function(e) {
  e.preventDefault();
  var text = e.clipboardData.getData("text/plain");
  document.execCommand("insertHTML", false, text);
});

Here another example for all containers in the body:

let allEditableDivs = document.querySelectorAll('div[contenteditable="true"]');

[].forEach.call(allEditableDivs, function (el) {
  el.addEventListener('paste', function(e) {
    e.preventDefault();
    var text = e.clipboardData.getData("text/plain");
    document.execCommand("insertHTML", false, text);
  }, false);
}

Saludos.

Tanner Stern
  • 128
  • 1
  • 8
Isaac Limón
  • 1,940
  • 18
  • 15
  • 3
    The key to this answer is the use of the clipboardData.getData("text/plain") API. Pretty self-explanatory I thought. More info on the clipboard API here: https://www.w3.org/TR/clipboard-apis/ – Paul McClean Jun 27 '17 at 10:31
  • Nice! Can someone add some words about how 'document.execCommand("insertHTML", false, text);' works? I call a method on document but somehow it inserts at "the current position"... – andymel Apr 28 '20 at 18:40
  • 2
    This works for copy-paste, but I found that users can still abuse the field via drag-and-drop. – Mitch Talmadge May 21 '21 at 18:50
  • 1
    execCommand is deprecated, maybe there is another alternative to it? – Georgi Georgiev Mar 21 '23 at 14:34
  • this approach has the limitation that when you type 'hello ' and paste it, the empty space gets removed (because it's encoded as nbsp) which is confusing and bad UX – okram Jul 12 '23 at 11:21
32

Have you tried using innerText?

ADDED:

If you want to strip markup from content pasted into the editable div, try the old hack of creating a temporary div -- see example below.

<!DOCTYPE html>
<html>

<head> 
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
  <title>Strip editable div markup</title>

  <script type="text/javascript">
    function strip(html) {
      var tempDiv = document.createElement("DIV");
      tempDiv.innerHTML = html;
      return tempDiv.innerText;
    }
  </script>
</head>

<body>
  <div id="editableDiv" contentEditable="true"></div>
  <input type="button" value="press" onclick="alert(strip(document.getElementById('editableDiv').innerText));" />
</body>

</html>
Draško Kokić
  • 1,280
  • 1
  • 19
  • 34
Sam Dutton
  • 14,775
  • 6
  • 54
  • 64
  • How would I remove formatting from the copy and pasted text by using innerText? I thought I either have to set something on the contentEditable Div or remove the formatting from the clipboard before the text is pasted. Thanks for your help. – Garth Humphreys Aug 01 '11 at 14:49
  • 2
    Sorry didn't understand that you wanted to paste text *into* the editable div. See edited answer for suggestion to remove formatting. – Sam Dutton Aug 01 '11 at 15:07
  • 1
    That's going to remove formatting from the whole editable div, not just the stuff pasted in. If that's what the OP wants then he could use a textarea instead. – Tim Down Aug 01 '11 at 22:06
  • I'm building a rich text editor and want to implement a function which removes the formatting of a selected text. Any idea how to do this? (I'm using a contentEditable div) – ThE uSeFuL Jun 13 '12 at 10:48
  • 1
    This is genius! Just saved my bacon. – Doug Wolfgram Oct 12 '13 at 05:28
  • Is there any way to do this and preserve linefeeds? – Doug Wolfgram Nov 16 '13 at 08:37
  • Just a warning, this strips out newlines. I wouldn't recommend this solution. – Eric Wood Feb 07 '14 at 18:06
  • chrome has `contenteditable="plaintext-only"` but using textContent is cross-browser – caub Aug 03 '16 at 18:22
14

Try <div id="editableDiv" contentEditable="plaintext-only"></div>

Matthias Hagemann
  • 1,328
  • 1
  • 12
  • 13
  • 6
    I would love to use this, but it is not (yet) widely supported. https://caniuse.com/#feat=mdn-html_global_attributes_contenteditable_plaintext-only – JustinStolle Jun 08 '20 at 20:23
  • No support in Firefox. At the moment you can't get around `true` if you want to support all browsers and have to make friends with a Javascript solution. :( – qräbnö Apr 26 '21 at 21:26
12

Was looking for answer to this for ages and ended up writing my own.

I hope this helps others. At the time of writing this it appears to work in ie9, latest chrome and firefox.

<div contenteditable="true" onpaste="OnPaste_StripFormatting(this, event);" />

<script type="text/javascript">

    var _onPaste_StripFormatting_IEPaste = false;

    function OnPaste_StripFormatting(elem, e) {

        if (e.originalEvent && e.originalEvent.clipboardData && e.originalEvent.clipboardData.getData) {
            e.preventDefault();
            var text = e.originalEvent.clipboardData.getData('text/plain');
            window.document.execCommand('insertText', false, text);
        }
        else if (e.clipboardData && e.clipboardData.getData) {
            e.preventDefault();
            var text = e.clipboardData.getData('text/plain');
            window.document.execCommand('insertText', false, text);
        }
        else if (window.clipboardData && window.clipboardData.getData) {
            // Stop stack overflow
            if (!_onPaste_StripFormatting_IEPaste) {
                _onPaste_StripFormatting_IEPaste = true;
                e.preventDefault();
                window.document.execCommand('ms-pasteTextOnly', false);
            }
            _onPaste_StripFormatting_IEPaste = false;
        }

    }

</script>
Draško Kokić
  • 1,280
  • 1
  • 19
  • 34
  • Seems to work great and it preserves newline formatting. Thanks! :) – user2173353 Oct 29 '15 at 14:13
  • For some reason I get an error of “Invalid argument.” on IE8 with this. I had once the impression it was working there, but probably I was wrong. I don't know if some other event (or something) screws things up... Good otherwise. :) – user2173353 Nov 24 '15 at 07:00
6

I know it's been a while, but I had the same problem. On my case, it's a GWT application to make it even worse. Anyway, resolved the problem with:

var clearText = event.clipboardData.getData('text/plain');
document.execCommand('inserttext', false, clearText);

See: https://jsfiddle.net/erikwoods/Ee3yC/

I preferred "inserttext" command instead of "insertHTML", because the documentation says it's exactly to insert plain text, so seems more suitable. See https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Robson Hermes
  • 425
  • 5
  • 12
3

With Jquery you can use .text() method, so, when blur for example you can replace the content with the text content

$("#element").blur(function(e) {
    $(this).html($(this).text());
});
Justo
  • 1,793
  • 1
  • 10
  • 6
0

I'd like to add my solution to this issue:

ContentEditableElement.addEventListener('input', function(ev) {
  if(ev.target.innerHTML != ev.target.textContent) {

    // determine position of the text caret
    var caretPos = 0,
      sel, range;
    sel = window.getSelection();
    if (sel.rangeCount) {
      range = sel.getRangeAt(0);
      var children = ev.target.childNodes;
      var keepLooping = true;
      for(let i = 0; keepLooping; i++) {
        if(children[i] == range.commonAncestorContainer || children[i] == range.commonAncestorContainer.parentNode) {
          caretPos += range.endOffset;
          keepLooping = false;
        } else {
          caretPos += children[i].textContent.length;
        }
      }

      // set the element's innerHTML to its textContent
      ev.target.innerHTML = ev.target.textContent;

      // put the caret where it was before
      range = document.createRange();
      range.setStart(ev.target.childNodes[0], caretPos);
      range.collapse(true);
      sel.removeAllRanges();
      sel.addRange(range);
    }
  }
});

(this isn't compatible with older versions of IE)

Jacob
  • 527
  • 1
  • 5
  • 14
0

Just for my later life. ;)

styles.css

/* Not easy to look exactly like input field: */
/* https://stackoverflow.com/a/8957518/1707015 */
.contenteditable_div {
    /* box-shadow: 1px 1px 1px 0 lightgray inset; */
    background-color:#dddddd;
    overflow-wrap:break-word;
    padding:3px;
}

index.html

<!-- Firefox doesn't support contenteditable="plaintext-only" yet! -->
<div class="contenteditable_div" contenteditable="true" id="blubbi">abc</div>

script.js

// Optional: Copy font from other input field:
// $('#blubbi').css('font', $('#blubbi_input_field').css('font'));

$('.contenteditable_div').on('input', function(){
    // problems with setting cursor to beginning of div!
    // this.innerHTML = this.innerText;
    $(this).text($(this).text());
});

Keep in mind that this solution doesn't support or care about line breaks.

And keep in mind that setting the text like this will set the cursor to the beginning of your contenteditable div - while you are typing. Still a good solution if you need it only for copy & paste. Please write a comment if you have an easy solution for this "reverse typing problem" (under 10 lines of code please). ;)

Tested on Firefox 89, Chrome 90 and Safari 14.

qräbnö
  • 2,722
  • 27
  • 40
0

You can't access the system clipboard so you'll need a hack. See this question: JavaScript get clipboard data on paste event (Cross browser)

Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536