Plain Text Paste with JavaScript — Apr 10, 2013, 8:45 pm
This was driving me nuts! I wanted to use a DIV with contenteditable=true, which of course is rich text featured. That is certainly a good thing, but what if you don't want pasted tags to screw up the design in the editable box?
A simple document.execCommand("paste") of course doesn't do it.

I found two different approaches and learned, that there is no clean way to do it.

#1 from Stackoverflow unfortunately didn't work. Chrome didn't know how to handle getData().
editor.addEventListener("paste", function(e) {
    // cancel paste
    e.preventDefault();
    // get text representation of clipboard
    var text = e.clipboardData.getData("text/plain");
    // insert text manually
    document.execCommand("insertHTML", false, text);
});

Another slidely different method did not do the job either (Stackoverflow):
document.querySelector("div[contenteditable]").addEventListener("paste", function(e) {
    e.preventDefault();
    var text = e.clipboardData.getData("text/plain");
    var temp = document.createElement("div");
    temp.innerHTML = text;
    document.execCommand("insertHTML", false, temp.textContent);
});​


#2 also from Stackoverflow uses a hidden textarea to clean up the code. It's a nice idea and I found out, that the textarea must not be display:none or visibility:hidden. However an opacity:0 or height/width:0px leaves it funtional. It filled my hidden textarea with plain text, but pasted HTML in my DIV in the beginning.
$('.editable').live('paste', function()
{
    var $this = $(this);
    //more code here to remember caret position, etc
    $('#clipboard').val('').focus(); //put the focus in the hidden textarea so that, when the paste actually occurs, it's auto-sanitized by the textarea
    setTimeout(function() //then this will be executed immediately after the paste actually occurs
    {
        $this.focus();
        document.execCommand('insertHTML', true, $('#clipboard').val());
    });
});

I used it like this and it worked for me then:
$('#editor').bind('paste', function(e){
	var $this = $(this);
	$('#clipboard').val('').focus();
	setTimeout(function() {
		$this.focus();
		document.execCommand('insertHTML', true, $('#clipboard').val());
	});
});
The problem: It always adds content to the beginning of my DIV, because it switches focus inbetween. I tried some experiments with getSelection() to save the pointer-position with the range, but I finally gave up...

...and started all over again with a great idea I got. It is a lot less browser dependable. The execCommands's are nice, but sometimes very restricted and different in browsers.

Here is my solution:
$('#editor').bind('paste', function(){ // catch the paste-event in the DIV
	// get content before paste
	var before = document.getElementById('editor').innerHTML;
	setTimeout(function(){
		// get content after paste by a 100ms delay
		var after = document.getElementById('editor').innerHTML;
		// find the start and end position where the two differ
		var pos1 = -1;
		var pos2 = -1;
		for (var i=0; i<after.length; i++) {
			if (pos1 == -1 && before.substr(i, 1) != after.substr(i, 1)) pos1 = i;
			if (pos2 == -1 && before.substr(before.length-i-1, 1) != after.substr(after.length-i-1, 1)) pos2 = i;
		}
		// the difference = pasted string with HTML:
		var pasted = after.substr(pos1, after.length-pos2-pos1);
		// strip the tags:
		var replace = pasted.replace(/<[^>]+>/g, '');
		// build clean content:
		var replaced = after.substr(0, pos1)+replace+after.substr(pos1+pasted.length);
		// replace the HTML mess with the plain content
		document.getElementById('editor').innerHTML = replaced;
	}, 100);
});
It works perfectly! And it automatically places the pointer behind the pasted & cleaned content!

I had to do a small correction for the positioning:
var pasted = after.substr(pos1-1, after.length-pos2-pos1+2);
var replaced = after.substr(0, pos1-1)+replace+after.substr(pos1-1+pasted.length);
After taking one more surrounding character, it didn't fail to replace tags after a new line.
Roman on May 16, 2013, 1:22 pm:
Thanks for the solution, really neat and handy and works like a charm on most IE and FF versions. But there's a 'but' :
'undo' functionality in IE for instance enables to revert modification to innerhtml and reveal the nasty original content with its formatting. Looking for the way to fix the latter.
Albert on May 21, 2013, 1:30 pm:
I can't really think of a nice solution for that at the moment. For a not-so-nice solution you could probably put the $('editor').bind()-content in a user defined function and also call this function with setInterval, i.e. like that:
function replaceTags() {
  // content of $('editor').bind() from above
}

setInterval(function() {
  // check for tags every 0.1 seconds
  if (document.getElementById('editor').innerHTML.match(/<[^>]+>/)) replaceTags();
}, 100);

$('#editor').bind('paste', replaceTags);

I know, it's definitely not nice, it just blocks the user's possibilities
Saurabh Madan on Jun 10, 2013, 8:51 pm:
Instead of going through each character, you could just do a find for your pasted value. That will help you get rid of the for loop which counts by character.
So, something like this should save some processing time.

var Pos1 = before.indexOf(after) - 1;
var Pos2 = before.indexOf(after.length) - 1;
if (Pos1 < 1) { Pos1 = 0; Pos2 = 0; }

Jian on Aug 27, 2013, 8:57 am:
Thanks for the solution. But I think there is a bug in Positioning correction. You need to add boundary check for pos1 and pos2.

  if(pos1 > 0) pos1 -= 1;
  if(pos2 > 0) pos2 -= 1;
ncomFF on May 10, 2014, 6:37 pm:
Your solutions is very good!
Mikesh on May 31, 2014, 12:37 pm:
Seems like a good solution.
But I have a major problem. When I paste some formated Text in a new line or after a space, it will be deleted. If I paste it at the beginning or between current text it works...

Any idea?
Mikesh on May 31, 2014, 12:39 pm:
..I should say, that it only happens in Firefox. In Chrome and IE it works fine.
David Damasceno on Aug 9, 2015, 1:05 am:
Look my response:
$('[contenteditable]').on('paste', function (e) {
                            e.preventDefault();
                            var cd =  e.originalEvent.clipboardData;
                            $("[contenteditable]").empty().text(cd.getData("text/plain"));
                        });
Adriano on Jan 6, 2016, 7:40 am:
Hi, good post tahnks, its Work on all browsers?
Enter your comment:


  Use [code=LANGUAGE]...[/code] for highlighting (i.e. html, php, css, js)