17

I have a contentEditable and I strip the formatting of pasted content on('paste') by catching the event. Then I focus a textarea, paste the content in there, and copy the value. Pretty much the answer from here. The problem is that I can’t do this:

$("#paste-output").text($("#area").val());

because that would replace my entire content with the pasted text. So I need to paste the content at caret position. I put together a script that does that:

pasteHtmlAtCaret($("#area").val());

// pastes CTRL+V content at caret position
function pasteHtmlAtCaret(html) {
  var sel, range;
  if (window.getSelection) {
    sel = window.getSelection();
    if (sel.getRangeAt && sel.rangeCount) {
      range = sel.getRangeAt(0);
      range.deleteContents();
      var el = document.createElement("div");
      el.innerHTML = html;
      var frag = document.createDocumentFragment(), node, lastNode;
      while ((node = el.firstChild)) {
        lastNode = frag.appendChild(node);
      }
      range.insertNode(frag);

      if (lastNode) {
        range = range.cloneRange();
        range.setStartAfter(lastNode);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
      }
    }
  } else if (document.selection && document.selection.type != "Control") {
    document.selection.createRange().pasteHTML(html);
  }
}

The only problem is that it pastes HTML content, at caret position using the html variable. How can I transform that into plain text? I tried adding the jQuery .text(html) to variable without luck. Something like this might help:

el.textContent||el.innerText;

Any ideas or a better solution? Thanks!


EDIT: Thanks to the answers below I modified my code and solved the issue. I basically copied the value of textarea into a div and grabbed only its .text():

// on paste, strip clipboard from HTML tags if any
$('#post_title, #post_content').on("paste", function() {
    var text = $('<div>').html($("#area").val()).text();
    pasteHtmlAtCaret(text);
  }, 20);
});
Community
  • 1
  • 1
Alex
  • 1,312
  • 3
  • 14
  • 31
  • Could you please post your solution (from the edit at the bottom of your post) as an answer? It _is_ an answer and solution, so it makes it easier for readers - plus I intend to upvote it. – doppelgreener Jun 25 '13 at 03:19
  • Will this solution work if you have a range selected? AKA not just inserting at once place but instead replacing the selected region? – whiterook6 Oct 10 '14 at 21:28

4 Answers4

10
  1. Create an external div,
  2. Put your html in that div,
  3. Copy the text from that div
  4. Insert it at the cursor's position.
doppelgreener
  • 5,752
  • 8
  • 42
  • 59
Shehabic
  • 6,347
  • 6
  • 46
  • 91
  • 2
    That's correct. Using jquery: `$('
    ').html(htmlSnippet).text()`
    – Felipe Castro Feb 28 '13 at 00:36
  • 1
    This is weird. why would you do that when you can use the same contenteditable div to just do the same and re-populate it with the sanitized content? **and** in any case, the unsanitized content will flash for a brief moment. – vsync Jul 08 '14 at 20:08
  • Hi, does this preserve line breaks for you? – AshD Nov 30 '17 at 01:39
3

Upon request from Jonathan Hobbs I am posting this as the answer. Thanks to the answers above I modified my code and solved the issue. I basically copied the value of textarea into a div and grabbed only its .text():

// on paste, strip clipboard from HTML tags if any
$('#post_title, #post_content').on("paste", function() {
  setTimeout(function(){
    var text = $('<div>').html($("#area").val()).text();
    pasteHtmlAtCaret(text);
  }, 20);
});
programmer5000
  • 888
  • 11
  • 32
Alex
  • 1,312
  • 3
  • 14
  • 31
  • 4
    your example code has been copy-pasted incorrectly it would seem. There is a need for a `setTimeout` to let the content to actually be in that div (it's stupid but it's true) and in your code, we can see there is a `20` but the timeout isn't included, therefor this code will cause errors, until fixed. – vsync Jul 08 '14 at 20:10
  • I edited that at http://stackoverflow.com/review/suggested-edits/15338834 – programmer5000 Feb 25 '17 at 16:17
2

Replace tag solution:

http://jsfiddle.net/tomwan/cbp1u2cx/1/

var $plainText = $("#plainText");
var $linkOnly = $("#linkOnly");
var $html = $("#html");

$plainText.on('paste', function (e) {
    window.setTimeout(function () {
        $plainText.html(removeAllTags(replaceStyleAttr($plainText.html())));
    }, 0);
});

$linkOnly.on('paste', function (e) {
    window.setTimeout(function () {
        $linkOnly.html(removeTagsExcludeA(replaceStyleAttr($linkOnly.html())));
    }, 0);
});

function replaceStyleAttr (str) {
    return str.replace(/(<[\w\W]*?)(style)([\w\W]*?>)/g, function (a, b, c, d) {
        return b + 'style_replace' + d;
    });
}

function removeTagsExcludeA (str) {
    return str.replace(/<\/?((?!a)(\w+))\s*[\w\W]*?>/g, '');
}

function removeAllTags (str) {
    return str.replace(/<\/?(\w+)\s*[\w\W]*?>/g, '');
}
TomWan
  • 209
  • 1
  • 9
1

The solution I used is to set the innerText of the element on blur:

$element.on('blur', () => this.innerText = this.innerText);

This keeps the whitespace, but strips the formatting. It may work acceptable in your scenario as well.

Sebazzz
  • 906
  • 1
  • 11
  • 30