[javascript] JavaScript get clipboard data on paste event (Cross browser)

How can a web application detect a paste event and retrieve the data to be pasted?

I would like to remove HTML content before the text is pasted into a rich text editor.

Cleaning the text after being pasted afterwards works, but the problem is that all previous formatting is lost. For example, I can write a sentence in the editor and make it bold, but when I paste new text, all formatting is lost. I want to clean just the text that is pasted, and leave any previous formatting untouched.

Ideally, the solution should work across all modern browsers (e.g., MSIE, Gecko, Chrome, and Safari).

Note that MSIE has clipboardData.getData(), but I could not find similar functionality for other browsers.

This question is related to javascript cross-browser clipboard

The answer is


Just let the browser paste as usual in its content editable div and then after the paste swap any span elements used for custom text styles with the text itself. This seems to work okay in internet explorer and the other browsers I tried...

$('[contenteditable]').on('paste', function (e) {
    setTimeout(function () {
        $(e.target).children('span').each(function () {
            $(this).replaceWith($(this).text());
        });
    }, 0);
});

This solution assumes that you are running jQuery and that you don't want text formatting in any of your content editable divs.

The plus side is that it's super simple.


I've written a little proof of concept for Tim Downs proposal here with off-screen textarea. And here goes the code:

<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script> 
<script language="JavaScript">
 $(document).ready(function()
{

var ctrlDown = false;
var ctrlKey = 17, vKey = 86, cKey = 67;

$(document).keydown(function(e)
{
    if (e.keyCode == ctrlKey) ctrlDown = true;
}).keyup(function(e)
{
    if (e.keyCode == ctrlKey) ctrlDown = false;
});

$(".capture-paste").keydown(function(e)
{
    if (ctrlDown && (e.keyCode == vKey || e.keyCode == cKey)){
        $("#area").css("display","block");
        $("#area").focus();         
    }
});

$(".capture-paste").keyup(function(e)
{
    if (ctrlDown && (e.keyCode == vKey || e.keyCode == cKey)){                      
        $("#area").blur();
        //do your sanitation check or whatever stuff here
        $("#paste-output").text($("#area").val());
        $("#area").val("");
        $("#area").css("display","none");
    }
});

});
</script>

</head>
<body class="capture-paste">

<div id="paste-output"></div>


    <div>
    <textarea id="area" style="display: none; position: absolute; left: -99em;"></textarea>
    </div>

</body>
</html>

Just copy and paste the whole code into one html file and try to paste (using ctrl-v) text from clipboard anywhere on the document.

I've tested it in IE9 and new versions of Firefox, Chrome and Opera. Works quite well. Also it's good that one can use whatever key combination he prefers to triger this functionality. Of course don't forget to include jQuery sources.

Feel free to use this code and if you come with some improvements or problems please post them back. Also note that I'm no Javascript developer so I may have missed something (=>do your own testign).


This was too long for a comment on Nico's answer, which I don't think works on Firefox any more (per the comments), and didn't work for me on Safari as is.

Firstly, you now appear to be able to read directly from the clipboard. Rather than code like:

if (/text\/plain/.test(e.clipboardData.types)) {
    // shouldn't this be writing to elem.value for text/plain anyway?
    elem.innerHTML = e.clipboardData.getData('text/plain');
}

use:

types = e.clipboardData.types;
if (((types instanceof DOMStringList) && types.contains("text/plain")) ||
    (/text\/plain/.test(types))) {
    // shouldn't this be writing to elem.value for text/plain anyway?
    elem.innerHTML = e.clipboardData.getData('text/plain');
}

because Firefox has a types field which is a DOMStringList which does not implement test.

Next Firefox will not allow paste unless the focus is in a contenteditable=true field.

Finally, Firefox will not allow paste reliably unless the focus is in a textarea (or perhaps input) which is not only contenteditable=true but also:

  • not display:none
  • not visibility:hidden
  • not zero sized

I was trying to hide the text field so I could make paste work over a JS VNC emulator (i.e. it was going to a remote client and there was no actually textarea etc to paste into). I found trying to hide the text field in the above gave symptoms where it worked sometimes, but typically failed on the second paste (or when the field was cleared to prevent pasting the same data twice) as the field lost focus and would not properly regain it despite focus(). The solution I came up with was to put it at z-order: -1000, make it display:none, make it as 1px by 1px, and set all the colours to transparent. Yuck.

On Safari, you the second part of the above applies, i.e. you need to have a textarea which is not display:none.


Based on l2aelba anwser. This was tested on FF, Safari, Chrome, IE (8,9,10 and 11)

    $("#editText").on("paste", function (e) {
        e.preventDefault();

        var text;
        var clp = (e.originalEvent || e).clipboardData;
        if (clp === undefined || clp === null) {
            text = window.clipboardData.getData("text") || "";
            if (text !== "") {
                if (window.getSelection) {
                    var newNode = document.createElement("span");
                    newNode.innerHTML = text;
                    window.getSelection().getRangeAt(0).insertNode(newNode);
                } else {
                    document.selection.createRange().pasteHTML(text);
                }
            }
        } else {
            text = clp.getData('text/plain') || "";
            if (text !== "") {
                document.execCommand('insertText', false, text);
            }
        }
    });

Solution that works for me is adding event listener to paste event if you are pasting to a text input. Since paste event happens before text in input changes, inside my on paste handler I create a deferred function inside which I check for changes in my input box that happened on paste:

onPaste: function() {
    var oThis = this;
    setTimeout(function() { // Defer until onPaste() is done
        console.log('paste', oThis.input.value);
        // Manipulate pasted input
    }, 1);
}

Solution #1 (Plain Text only and requires Firefox 22+)

Works for IE6+, FF 22+, Chrome, Safari, Edge (Only tested in IE9+, but should work for lower versions)

If you need support for pasting HTML or Firefox <= 22, see Solution #2.

HTML

<div id='editableDiv' contenteditable='true'>Paste</div>

JavaScript

function handlePaste (e) {
    var clipboardData, pastedData;

    // Stop data actually being pasted into div
    e.stopPropagation();
    e.preventDefault();

    // Get pasted data via clipboard API
    clipboardData = e.clipboardData || window.clipboardData;
    pastedData = clipboardData.getData('Text');
    
    // Do whatever with pasteddata
    alert(pastedData);
}

document.getElementById('editableDiv').addEventListener('paste', handlePaste);

JSFiddle: https://jsfiddle.net/swL8ftLs/12/

Note that this solution uses the parameter 'Text' for the getData function, which is non-standard. However, it works in all browsers at the time of writing.


Solution #2 (HTML and works for Firefox <= 22)

Tested in IE6+, FF 3.5+, Chrome, Safari, Edge

HTML

<div id='div' contenteditable='true'>Paste</div>

JavaScript

var editableDiv = document.getElementById('editableDiv');

function handlepaste (e) {
    var types, pastedData, savedContent;
    
    // Browsers that support the 'text/html' type in the Clipboard API (Chrome, Firefox 22+)
    if (e && e.clipboardData && e.clipboardData.types && e.clipboardData.getData) {
            
        // Check for 'text/html' in types list. See abligh's answer below for deatils on
        // why the DOMStringList bit is needed. We cannot fall back to 'text/plain' as
        // Safari/Edge don't advertise HTML data even if it is available
        types = e.clipboardData.types;
        if (((types instanceof DOMStringList) && types.contains("text/html")) || (types.indexOf && types.indexOf('text/html') !== -1)) {
        
            // Extract data and pass it to callback
            pastedData = e.clipboardData.getData('text/html');
            processPaste(editableDiv, pastedData);

            // Stop the data from actually being pasted
            e.stopPropagation();
            e.preventDefault();
            return false;
        }
    }
    
    // Everything else: Move existing element contents to a DocumentFragment for safekeeping
    savedContent = document.createDocumentFragment();
    while(editableDiv.childNodes.length > 0) {
        savedContent.appendChild(editableDiv.childNodes[0]);
    }
    
    // Then wait for browser to paste content into it and cleanup
    waitForPastedData(editableDiv, savedContent);
    return true;
}

function waitForPastedData (elem, savedContent) {

    // If data has been processes by browser, process it
    if (elem.childNodes && elem.childNodes.length > 0) {
    
        // Retrieve pasted content via innerHTML
        // (Alternatively loop through elem.childNodes or elem.getElementsByTagName here)
        var pastedData = elem.innerHTML;
        
        // Restore saved content
        elem.innerHTML = "";
        elem.appendChild(savedContent);
        
        // Call callback
        processPaste(elem, pastedData);
    }
    
    // Else wait 20ms and try again
    else {
        setTimeout(function () {
            waitForPastedData(elem, savedContent)
        }, 20);
    }
}

function processPaste (elem, pastedData) {
    // Do whatever with gathered data;
    alert(pastedData);
    elem.focus();
}

// Modern browsers. Note: 3rd argument is required for Firefox <= 6
if (editableDiv.addEventListener) {
    editableDiv.addEventListener('paste', handlepaste, false);
}
// IE <= 8
else {
    editableDiv.attachEvent('onpaste', handlepaste);
}

JSFiddle: https://jsfiddle.net/nicoburns/wrqmuabo/23/

Explanation

The onpaste event of the div has the handlePaste function attached to it and passed a single argument: the event object for the paste event. Of particular interest to us is the clipboardData property of this event which enables clipboard access in non-ie browsers. In IE the equivalent is window.clipboardData, although this has a slightly different API.

See resources section below.


The handlepaste function:

This function has two branches.

The first checks for the existence of event.clipboardData and checks whether it's types property contains 'text/html' (types may be either a DOMStringList which is checked using the contains method, or a string which is checked using the indexOf method). If all of these conditions are fulfilled, then we proceed as in solution #1, except with 'text/html' instead of 'text/plain'. This currently works in Chrome and Firefox 22+.

If this method is not supported (all other browsers), then we

  1. Save the element's contents to a DocumentFragment
  2. Empty the element
  3. Call the waitForPastedData function

The waitforpastedata function:

This function first polls for the pasted data (once per 20ms), which is necessary because it doesn't appear straight away. When the data has appeared it:

  1. Saves the innerHTML of the editable div (which is now the pasted data) to a variable
  2. Restores the content saved in the DocumentFragment
  3. Calls the 'processPaste' function with the retrieved data

The processpaste function:

Does arbitrary things with the pasted data. In this case we just alert the data, you can do whatever you like. You will probably want to run the pasted data through some kind of data sanitising process.


Saving and restoring the cursor position

In a real sitution you would probably want to save the selection before, and restore it afterwards (Set cursor position on contentEditable <div>). You could then insert the pasted data at the position the cursor was in when the user initiated the paste action.

Resources:

Thanks to Tim Down to suggesting the use of a DocumentFragment, and abligh for catching an error in Firefox due to the use of DOMStringList instead of a string for clipboardData.types


This worked for me :

function onPasteMe(currentData, maxLen) {
    // validate max length of pasted text
    var totalCharacterCount = window.clipboardData.getData('Text').length;
}

<input type="text" onPaste="return onPasteMe(this, 50);" />

Simple solution:

document.onpaste = function(e) {
    var pasted = e.clipboardData.getData('Text');
    console.log(pasted)
}

$('#dom').on('paste',function (e){
    setTimeout(function(){
        console.log(e.currentTarget.value);
    },0);
});

Simple version:

document.querySelector('[contenteditable]').addEventListener('paste', (e) => {
    e.preventDefault();
    const text = (e.originalEvent || e).clipboardData.getData('text/plain');
    window.document.execCommand('insertText', false, text);
});

Using clipboardData

Demo : http://jsbin.com/nozifexasu/edit?js,output

Edge, Firefox, Chrome, Safari, Opera tested.

? Document.execCommand() is obsolete now.


Note: Remember to check input/output at server-side also (like PHP strip-tags)


This is an existing code posted above but I have updated it for IE's, the bug was when the existing text is selected and pasted will not delete the selected content. This has been fixed by the below code

selRange.deleteContents(); 

See complete code below

$('[contenteditable]').on('paste', function (e) {
    e.preventDefault();

    if (window.clipboardData) {
        content = window.clipboardData.getData('Text');        
        if (window.getSelection) {
            var selObj = window.getSelection();
            var selRange = selObj.getRangeAt(0);
            selRange.deleteContents();                
            selRange.insertNode(document.createTextNode(content));
        }
    } else if (e.originalEvent.clipboardData) {
        content = (e.originalEvent || e).clipboardData.getData('text/plain');
        document.execCommand('insertText', false, content);
    }        
});

For cleaning the pasted text and replacing the currently selected text with the pasted text the matter is pretty trivial:

<div id='div' contenteditable='true' onpaste='handlepaste(this, event)'>Paste</div>

JS:

function handlepaste(el, e) {
  document.execCommand('insertText', false, e.clipboardData.getData('text/plain'));
  e.preventDefault();
}

You can do this in this way:

use this jQuery plugin for pre & post paste events:

$.fn.pasteEvents = function( delay ) {
    if (delay == undefined) delay = 20;
    return $(this).each(function() {
        var $el = $(this);
        $el.on("paste", function() {
            $el.trigger("prepaste");
            setTimeout(function() { $el.trigger("postpaste"); }, delay);
        });
    });
};

Now you can use this plugin;:

$('#txt').on("prepaste", function() { 

    $(this).find("*").each(function(){

        var tmp=new Date.getTime();
        $(this).data("uid",tmp);
    });


}).pasteEvents();

$('#txt').on("postpaste", function() { 


  $(this).find("*").each(function(){

     if(!$(this).data("uid")){
        $(this).removeClass();
          $(this).removeAttr("style id");
      }
    });
}).pasteEvents();

Explaination

First set a uid for all existing elements as data attribute.

Then compare all nodes POST PASTE event. So by comparing you can identify the newly inserted one because they will have a uid, then just remove style/class/id attribute from newly created elements, so that you can keep your older formatting.


function myFunct( e ){
    e.preventDefault();

    var pastedText = undefined;
    if( window.clipboardData && window.clipboardData.getData ){
    pastedText = window.clipboardData.getData('Text');
} 
else if( e.clipboardData && e.clipboardData.getData ){
    pastedText = e.clipboardData.getData('text/plain');
}

//work with text

}
document.onpaste = myFunct;

Live Demo

Tested on Chrome / FF / IE11

There is a Chrome/IE annoyance which is that these browsers add <div> element for each new line. There is a post about this here and it can be fixed by setting the contenteditable element to be display:inline-block

Select some highlighted HTML and paste it here:

_x000D_
_x000D_
function onPaste(e){_x000D_
  var content;_x000D_
  e.preventDefault();_x000D_
_x000D_
  if( e.clipboardData ){_x000D_
    content = e.clipboardData.getData('text/plain');_x000D_
    document.execCommand('insertText', false, content);_x000D_
    return false;_x000D_
  }_x000D_
  else if( window.clipboardData ){_x000D_
    content = window.clipboardData.getData('Text');_x000D_
    if (window.getSelection)_x000D_
      window.getSelection().getRangeAt(0).insertNode( document.createTextNode(content) );_x000D_
  }_x000D_
}_x000D_
_x000D_
_x000D_
/////// EVENT BINDING /////////_x000D_
document.querySelector('[contenteditable]').addEventListener('paste', onPaste);
_x000D_
[contenteditable]{ _x000D_
  /* chroem bug: https://stackoverflow.com/a/24689420/104380 */_x000D_
  display:inline-block;_x000D_
  width: calc(100% - 40px);_x000D_
  min-height:120px; _x000D_
  margin:10px;_x000D_
  padding:10px;_x000D_
  border:1px dashed green;_x000D_
}_x000D_
_x000D_
/* _x000D_
 mark HTML inside the "contenteditable"  _x000D_
 (Shouldn't be any OFC!)'_x000D_
*/_x000D_
[contenteditable] *{_x000D_
  background-color:red;_x000D_
}
_x000D_
<div contenteditable></div>
_x000D_
_x000D_
_x000D_


First that comes to mind is the pastehandler of google's closure lib http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/pastehandler.html


This one does not use any setTimeout().

I have used this great article to achieve cross browser support.

$(document).on("focus", "input[type=text],textarea", function (e) {
    var t = e.target;
    if (!$(t).data("EventListenerSet")) {
        //get length of field before paste
        var keyup = function () {
            $(this).data("lastLength", $(this).val().length);
        };
        $(t).data("lastLength", $(t).val().length);
        //catch paste event
        var paste = function () {
            $(this).data("paste", 1);//Opera 11.11+
        };
        //process modified data, if paste occured
        var func = function () {
            if ($(this).data("paste")) {
                alert(this.value.substr($(this).data("lastLength")));
                $(this).data("paste", 0);
                this.value = this.value.substr(0, $(this).data("lastLength"));
                $(t).data("lastLength", $(t).val().length);
            }
        };
        if (window.addEventListener) {
            t.addEventListener('keyup', keyup, false);
            t.addEventListener('paste', paste, false);
            t.addEventListener('input', func, false);
        }
        else {//IE
            t.attachEvent('onkeyup', function () {
                keyup.call(t);
            });
            t.attachEvent('onpaste', function () {
                paste.call(t);
            });
            t.attachEvent('onpropertychange', function () {
                func.call(t);
            });
        }
        $(t).data("EventListenerSet", 1);
    }
}); 

This code is extended with selection handle before paste: demo


This solution is replace the html tag, it's simple and cross-browser; check this jsfiddle: http://jsfiddle.net/tomwan/cbp1u2cx/1/, core code:

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, '');
}

notice: you should do some work about xss filter on the back side because this solution cannot filter strings like '<<>>'


This should work on all browsers that support the onpaste event and the mutation observer.

This solution goes a step beyond getting the text only, it actually allows you to edit the pasted content before it get pasted into an element.

It works by using contenteditable, onpaste event (supported by all major browsers) en mutation observers (supported by Chrome, Firefox and IE11+)

step 1

Create a HTML-element with contenteditable

<div contenteditable="true" id="target_paste_element"></div>

step 2

In your Javascript code add the following event

document.getElementById("target_paste_element").addEventListener("paste", pasteEventVerifierEditor.bind(window, pasteCallBack), false);

We need to bind pasteCallBack, since the mutation observer will be called asynchronously.

step 3

Add the following function to your code

function pasteEventVerifierEditor(callback, e)
{
   //is fired on a paste event. 
    //pastes content into another contenteditable div, mutation observer observes this, content get pasted, dom tree is copied and can be referenced through call back.
    //create temp div
    //save the caret position.
    savedCaret = saveSelection(document.getElementById("target_paste_element"));

    var tempDiv = document.createElement("div");
    tempDiv.id = "id_tempDiv_paste_editor";
    //tempDiv.style.display = "none";
    document.body.appendChild(tempDiv);
    tempDiv.contentEditable = "true";

    tempDiv.focus();

    //we have to wait for the change to occur.
    //attach a mutation observer
    if (window['MutationObserver'])
    {
        //this is new functionality
        //observer is present in firefox/chrome and IE11
        // select the target node
        // create an observer instance
        tempDiv.observer = new MutationObserver(pasteMutationObserver.bind(window, callback));
        // configuration of the observer:
        var config = { attributes: false, childList: true, characterData: true, subtree: true };

        // pass in the target node, as well as the observer options
        tempDiv.observer.observe(tempDiv, config);

    }   

}



function pasteMutationObserver(callback)
{

    document.getElementById("id_tempDiv_paste_editor").observer.disconnect();
    delete document.getElementById("id_tempDiv_paste_editor").observer;

    if (callback)
    {
        //return the copied dom tree to the supplied callback.
        //copy to avoid closures.
        callback.apply(document.getElementById("id_tempDiv_paste_editor").cloneNode(true));
    }
    document.body.removeChild(document.getElementById("id_tempDiv_paste_editor"));

}

function pasteCallBack()
{
    //paste the content into the element.
    restoreSelection(document.getElementById("target_paste_element"), savedCaret);
    delete savedCaret;

    pasteHtmlAtCaret(this.innerHTML, false, true);
}   


saveSelection = function(containerEl) {
if (containerEl == document.activeElement)
{
    var range = window.getSelection().getRangeAt(0);
    var preSelectionRange = range.cloneRange();
    preSelectionRange.selectNodeContents(containerEl);
    preSelectionRange.setEnd(range.startContainer, range.startOffset);
    var start = preSelectionRange.toString().length;

    return {
        start: start,
        end: start + range.toString().length
    };
}
};

restoreSelection = function(containerEl, savedSel) {
    containerEl.focus();
    var charIndex = 0, range = document.createRange();
    range.setStart(containerEl, 0);
    range.collapse(true);
    var nodeStack = [containerEl], node, foundStart = false, stop = false;

    while (!stop && (node = nodeStack.pop())) {
        if (node.nodeType == 3) {
            var nextCharIndex = charIndex + node.length;
            if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
                range.setStart(node, savedSel.start - charIndex);
                foundStart = true;
            }
            if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
                range.setEnd(node, savedSel.end - charIndex);
                stop = true;
            }
            charIndex = nextCharIndex;
        } else {
            var i = node.childNodes.length;
            while (i--) {
                nodeStack.push(node.childNodes[i]);
            }
        }
    }

    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
}

function pasteHtmlAtCaret(html, returnInNode, selectPastedContent) {
//function written by Tim Down

var sel, range;
if (window.getSelection) {
    // IE9 and non-IE
    sel = window.getSelection();
    if (sel.getRangeAt && sel.rangeCount) {
        range = sel.getRangeAt(0);
        range.deleteContents();

        // Range.createContextualFragment() would be useful here but is
        // only relatively recently standardized and is not supported in
        // some browsers (IE9, for one)
        var el = document.createElement("div");
        el.innerHTML = html;
        var frag = document.createDocumentFragment(), node, lastNode;
        while ( (node = el.firstChild) ) {
            lastNode = frag.appendChild(node);
        }
        var firstNode = frag.firstChild;
        range.insertNode(frag);

        // Preserve the selection
        if (lastNode) {
            range = range.cloneRange();
            if (returnInNode)
            {
                range.setStart(lastNode, 0); //this part is edited, set caret inside pasted node.
            }
            else
            {
                range.setStartAfter(lastNode); 
            }
            if (selectPastedContent) {
                range.setStartBefore(firstNode);
            } else {
                range.collapse(true);
            }
            sel.removeAllRanges();
            sel.addRange(range);
        }
    }
} else if ( (sel = document.selection) && sel.type != "Control") {
    // IE < 9
    var originalRange = sel.createRange();
    originalRange.collapse(true);
    sel.createRange().pasteHTML(html);
    if (selectPastedContent) {
        range = sel.createRange();
        range.setEndPoint("StartToStart", originalRange);
        range.select();
    }
}
}

What the code does:

  1. Somebody fires the paste event by using ctrl-v, contextmenu or other means
  2. In the paste event a new element with contenteditable is created (an element with contenteditable has elevated privileges)
  3. The caret position of the target element is saved.
  4. The focus is set to the new element
  5. The content gets pasted into the new element and is rendered in the DOM.
  6. The mutation observer catches this (it registers all changes to the dom tree and content). Then fires the mutation event.
  7. The dom of the pasted content gets cloned into a variable and returned to the callback. The temporary element is destroyed.
  8. The callback receives the cloned DOM. The caret is restored. You can edit this before you append it to your target. element. In this example I'm using Tim Downs functions for saving/restoring the caret and pasting HTML into the element.

Example

_x000D_
_x000D_
document.getElementById("target_paste_element").addEventListener("paste", pasteEventVerifierEditor.bind(window, pasteCallBack), false);_x000D_
_x000D_
_x000D_
function pasteEventVerifierEditor(callback, e) {_x000D_
  //is fired on a paste event. _x000D_
  //pastes content into another contenteditable div, mutation observer observes this, content get pasted, dom tree is copied and can be referenced through call back._x000D_
  //create temp div_x000D_
  //save the caret position._x000D_
  savedCaret = saveSelection(document.getElementById("target_paste_element"));_x000D_
_x000D_
  var tempDiv = document.createElement("div");_x000D_
  tempDiv.id = "id_tempDiv_paste_editor";_x000D_
  //tempDiv.style.display = "none";_x000D_
  document.body.appendChild(tempDiv);_x000D_
  tempDiv.contentEditable = "true";_x000D_
_x000D_
  tempDiv.focus();_x000D_
_x000D_
  //we have to wait for the change to occur._x000D_
  //attach a mutation observer_x000D_
  if (window['MutationObserver']) {_x000D_
    //this is new functionality_x000D_
    //observer is present in firefox/chrome and IE11_x000D_
    // select the target node_x000D_
    // create an observer instance_x000D_
    tempDiv.observer = new MutationObserver(pasteMutationObserver.bind(window, callback));_x000D_
    // configuration of the observer:_x000D_
    var config = {_x000D_
      attributes: false,_x000D_
      childList: true,_x000D_
      characterData: true,_x000D_
      subtree: true_x000D_
    };_x000D_
_x000D_
    // pass in the target node, as well as the observer options_x000D_
    tempDiv.observer.observe(tempDiv, config);_x000D_
_x000D_
  }_x000D_
_x000D_
}_x000D_
_x000D_
_x000D_
_x000D_
function pasteMutationObserver(callback) {_x000D_
_x000D_
  document.getElementById("id_tempDiv_paste_editor").observer.disconnect();_x000D_
  delete document.getElementById("id_tempDiv_paste_editor").observer;_x000D_
_x000D_
  if (callback) {_x000D_
    //return the copied dom tree to the supplied callback._x000D_
    //copy to avoid closures._x000D_
    callback.apply(document.getElementById("id_tempDiv_paste_editor").cloneNode(true));_x000D_
  }_x000D_
  document.body.removeChild(document.getElementById("id_tempDiv_paste_editor"));_x000D_
_x000D_
}_x000D_
_x000D_
function pasteCallBack() {_x000D_
  //paste the content into the element._x000D_
  restoreSelection(document.getElementById("target_paste_element"), savedCaret);_x000D_
  delete savedCaret;_x000D_
_x000D_
  //edit the copied content by slicing_x000D_
  pasteHtmlAtCaret(this.innerHTML.slice(3), false, true);_x000D_
}_x000D_
_x000D_
_x000D_
saveSelection = function(containerEl) {_x000D_
  if (containerEl == document.activeElement) {_x000D_
    var range = window.getSelection().getRangeAt(0);_x000D_
    var preSelectionRange = range.cloneRange();_x000D_
    preSelectionRange.selectNodeContents(containerEl);_x000D_
    preSelectionRange.setEnd(range.startContainer, range.startOffset);_x000D_
    var start = preSelectionRange.toString().length;_x000D_
_x000D_
    return {_x000D_
      start: start,_x000D_
      end: start + range.toString().length_x000D_
    };_x000D_
  }_x000D_
};_x000D_
_x000D_
restoreSelection = function(containerEl, savedSel) {_x000D_
  containerEl.focus();_x000D_
  var charIndex = 0,_x000D_
    range = document.createRange();_x000D_
  range.setStart(containerEl, 0);_x000D_
  range.collapse(true);_x000D_
  var nodeStack = [containerEl],_x000D_
    node, foundStart = false,_x000D_
    stop = false;_x000D_
_x000D_
  while (!stop && (node = nodeStack.pop())) {_x000D_
    if (node.nodeType == 3) {_x000D_
      var nextCharIndex = charIndex + node.length;_x000D_
      if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {_x000D_
        range.setStart(node, savedSel.start - charIndex);_x000D_
        foundStart = true;_x000D_
      }_x000D_
      if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {_x000D_
        range.setEnd(node, savedSel.end - charIndex);_x000D_
        stop = true;_x000D_
      }_x000D_
      charIndex = nextCharIndex;_x000D_
    } else {_x000D_
      var i = node.childNodes.length;_x000D_
      while (i--) {_x000D_
        nodeStack.push(node.childNodes[i]);_x000D_
      }_x000D_
    }_x000D_
  }_x000D_
_x000D_
  var sel = window.getSelection();_x000D_
  sel.removeAllRanges();_x000D_
  sel.addRange(range);_x000D_
}_x000D_
_x000D_
function pasteHtmlAtCaret(html, returnInNode, selectPastedContent) {_x000D_
  //function written by Tim Down_x000D_
_x000D_
  var sel, range;_x000D_
  if (window.getSelection) {_x000D_
    // IE9 and non-IE_x000D_
    sel = window.getSelection();_x000D_
    if (sel.getRangeAt && sel.rangeCount) {_x000D_
      range = sel.getRangeAt(0);_x000D_
      range.deleteContents();_x000D_
_x000D_
      // Range.createContextualFragment() would be useful here but is_x000D_
      // only relatively recently standardized and is not supported in_x000D_
      // some browsers (IE9, for one)_x000D_
      var el = document.createElement("div");_x000D_
      el.innerHTML = html;_x000D_
      var frag = document.createDocumentFragment(),_x000D_
        node, lastNode;_x000D_
      while ((node = el.firstChild)) {_x000D_
        lastNode = frag.appendChild(node);_x000D_
      }_x000D_
      var firstNode = frag.firstChild;_x000D_
      range.insertNode(frag);_x000D_
_x000D_
      // Preserve the selection_x000D_
      if (lastNode) {_x000D_
        range = range.cloneRange();_x000D_
        if (returnInNode) {_x000D_
          range.setStart(lastNode, 0); //this part is edited, set caret inside pasted node._x000D_
        } else {_x000D_
          range.setStartAfter(lastNode);_x000D_
        }_x000D_
        if (selectPastedContent) {_x000D_
          range.setStartBefore(firstNode);_x000D_
        } else {_x000D_
          range.collapse(true);_x000D_
        }_x000D_
        sel.removeAllRanges();_x000D_
        sel.addRange(range);_x000D_
      }_x000D_
    }_x000D_
  } else if ((sel = document.selection) && sel.type != "Control") {_x000D_
    // IE < 9_x000D_
    var originalRange = sel.createRange();_x000D_
    originalRange.collapse(true);_x000D_
    sel.createRange().pasteHTML(html);_x000D_
    if (selectPastedContent) {_x000D_
      range = sel.createRange();_x000D_
      range.setEndPoint("StartToStart", originalRange);_x000D_
      range.select();_x000D_
    }_x000D_
  }_x000D_
}
_x000D_
div {_x000D_
  border: 1px solid black;_x000D_
  height: 50px;_x000D_
  padding: 5px;_x000D_
}
_x000D_
<div contenteditable="true" id="target_paste_element"></div>
_x000D_
_x000D_
_x000D_


Many thanks to Tim Down See this post for the answer:

Get the pasted content on document on paste event


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to cross-browser

Show datalist labels but submit the actual value Stupid error: Failed to load resource: net::ERR_CACHE_MISS Click to call html How to Detect Browser Back Button event - Cross Browser How can I make window.showmodaldialog work in chrome 37? Cross-browser custom styling for file upload button Flexbox and Internet Explorer 11 (display:flex in <html>?) browser sessionStorage. share between tabs? How to know whether refresh button or browser back button is clicked in Firefox CSS Custom Dropdown Select that works across all browsers IE7+ FF Webkit

Examples related to clipboard

In reactJS, how to copy text to clipboard? Copy output of a JavaScript variable to the clipboard Leave out quotes when copying from cell How to Copy Text to Clip Board in Android? How does Trello access the user's clipboard? Copying a rsa public key to clipboard Excel VBA code to copy a specific string to clipboard How to make vim paste from (and copy to) system's clipboard? Python script to copy text to clipboard Copy to Clipboard for all Browsers using javascript