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.
We create the handlePaste function to get the clipboard data. First, we call stopPropagation to stop the propagation of the event to the parent and ancestor elements. Then we call preventDefault to prevent the default paste behavior. Then we can get the clipboard data from the e.
To get to your clipboard history at any time, press Windows logo key + V. From the clipboard history, you can paste and pin frequently used items by choosing an individual item from your clipboard menu. Pinning an item keeps it from being removed from the clipboard history to make room for new items.
The HTML DOM onpaste event occurs when some content pastes in an element. this event works on every element like in <p> element if contenteditable set true then we can paste content in <p> element. The HTML DOM onpaste event mostly used in input element type=”text”. It supports all HTML Elements.
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.
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);
<div id='editableDiv' contenteditable='true'>Paste</div>
JSFiddle
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.
Tested in IE6+, FF 3.5+, Chrome, Safari, Edge
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); }
<div id='div' contenteditable='true'>Paste</div>
JSFiddle
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
DocumentFragment
waitForPastedData
functionThe 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:
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 sanitizing process.
Saving and restoring the cursor position
In a real situation 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.
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
The situation has changed since writing this answer: now that Firefox has added support in version 22, all major browsers now support accessing the clipboard data in a paste event. See Nico Burns's answer for an example.
In the past this was not generally possible in a cross-browser way. The ideal would be to be able to get the pasted content via the paste
event, which is possible in recent browsers but not in some older browsers (in particular, Firefox < 22).
When you need to support older browsers, what you can do is quite involved and a bit of a hack that will work in Firefox 2+, IE 5.5+ and WebKit browsers such as Safari or Chrome. Recent versions of both TinyMCE and CKEditor use this technique:
designMode
off and call focus()
on the textarea, thus moving the caret and effectively redirecting the pastedesignMode
back on, restores the user selection and pastes the text in.Note that this will only work for keyboard paste events and not pastes from the context or edit menus. By the time the paste event fires, it's too late to redirect the caret into the textarea (in some browsers, at least).
In the unlikely event that you need to support Firefox 2, note that you'll need to place the textarea in the parent document rather than the WYSIWYG editor iframe's document in that browser.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With