How can a web application detect a paste event and retrieve the data to be pasted?
web应用程序如何检测粘贴事件并检索要粘贴的数据?
I would like to remove HTML content before the text is pasted into a rich text editor.
我想在文本被粘贴到一个富文本编辑器之前删除HTML内容。
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).
理想情况下,解决方案应该适用于所有现代浏览器(例如,MSIE、Gecko、Chrome和Safari)。
Note that MSIE has clipboardData.getData()
, but I could not find similar functionality for other browsers.
注意,MSIE有clipboardData.getData(),但我无法为其他浏览器找到类似的功能。
20 个解决方案
#1
128
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.
自编写这个答案以来,情况发生了变化:现在Firefox在第22版中添加了支持,所有主流浏览器现在都支持在粘贴事件中访问剪贴板数据。请参见Nico Burns的回答。
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).
在过去,这在跨浏览器的方式中是不可能的。理想的方法是通过粘贴事件获得粘贴内容,这在最近的浏览器中是可能的,但在一些老的浏览器中是不可能的(特别是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:
当您需要支持旧的浏览器时,您所能做的是相当复杂的,并且有一些可以在Firefox 2+、IE 5.5+和WebKit浏览器(如Safari或Chrome)中工作的黑客。TinyMCE和CKEditor的最新版本使用了这种技术:
- Detect a ctrl-v / shift-ins event using a keypress event handler
- 使用keypress事件处理程序检测ctrl-v / shift-ins事件。
- In that handler, save the current user selection, add a textarea element off-screen (say at left -1000px) to the document, turn
designMode
off and callfocus()
on the textarea, thus moving the caret and effectively redirecting the paste - 在该处理程序中,保存当前的用户选择,在文档中添加一个textarea元素(比如左-1000px),然后将designMode关闭,并在textarea上调用focus(),从而移动caret并有效地重定向粘贴。
- Set a very brief timer (say 1 millisecond) in the event handler to call another function that stores the textarea value, removes the textarea from the document, turns
designMode
back on, restores the user selection and pastes the text in. - 在事件处理程序中设置一个非常简短的计时器(比如1毫秒),以调用另一个存储textarea值的函数,从文档中移除textarea,将designMode返回,重新存储用户选择并粘贴文本。
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).
注意,这只适用于键盘粘贴事件,而不是来自上下文或编辑菜单的粘贴。当粘贴事件触发时,将插入符号重定向到textarea(至少在某些浏览器中)已经太晚了。
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.
在您需要支持Firefox 2的不太可能的事件中,请注意,您需要将textarea放置在父文档中,而不是在浏览器中放置WYSIWYG编辑器iframe的文档。
#2
245
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)
适用于IE6+、FF 22+、Chrome、Safari、Edge(只在IE9+中测试过,但应该适用于较低版本)
If you need support for pasting HTML or Firefox <= 22, see Solution #2.
如果您需要支持粘贴HTML或Firefox <= 22,请参见解决方案#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/
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.
注意,这个解决方案使用了getData函数的参数“Text”,这是非标准的。但是,它在所有的浏览器中都是在编写时工作的。
Solution #2 (HTML and works for Firefox <= 22)
Tested in IE6+, FF 3.5+, Chrome, Safari, Edge
测试在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/
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.
div的onpaste事件具有与它相连的handlePaste函数并传递一个参数:粘贴事件的事件对象。我们特别感兴趣的是这个事件的clipboardData属性,它允许在非ie浏览器中访问剪贴板。在IE中相当于窗口。clipboardData,尽管它有一个稍微不同的API。
See resources section below.
请参阅下面的参考资料部分。
The handlepaste
function:
handlepaste函数:
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+.
第一个检查是否存在事件。clipboardData并检查它的类型属性是否包含“文本/html”(类型可能是一个DOMStringList,它是使用包含方法检查的,或者是使用indexOf方法检查的字符串)。如果所有这些条件都满足了,那么我们就像在解决方案#1中一样,除了“text/html”而不是“text/plain”。目前在Chrome和Firefox 22+中工作。
If this method is not supported (all other browsers), then we
如果不支持此方法(所有其他浏览器),那么我们。
- Save the element's contents to a
DocumentFragment
- 将元素的内容保存到DocumentFragment。
- Empty the element
- 空元素
- Call the
waitForPastedData
function - 调用waitForPastedData函数
The waitforpastedata
function:
waitforpastedata函数:
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:
该函数首先对粘贴数据(每20ms一次)进行投票,这是必要的,因为它不会马上出现。当数据出现时:
- Saves the innerHTML of the editable div (which is now the pasted data) to a variable
- 将可编辑div(现在是粘贴数据)的innerHTML保存到一个变量中。
- Restores the content saved in the DocumentFragment
- 重新存储在DocumentFragment中保存的内容。
- Calls the 'processPaste' function with the retrieved data
- 用检索到的数据调用“processPaste”函数。
The processpaste
function:
processpaste函数:
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.
在实际情况中,您可能希望保存之前的选择,并在之后恢复它(在contentEditable上设置游标位置
Resources:
- MDN paste event: https://developer.mozilla.org/en-US/docs/Web/Events/paste
- MDN粘贴事件:https://developer.mozilla.org/en-US/docs/Web/Events/paste
- MSDN clipboard: https://msdn.microsoft.com/en-us/library/ms535220(v=vs.85).aspx
- MSDN剪贴板:https://msdn.microsoft.com/en-us/library/ms535220(v = vs.85). aspx
- MDN DocumentFragment: https://developer.mozilla.org/en/docs/Web/API/DocumentFragment
- MDN DocumentFragment:https://developer.mozilla.org/en/docs/Web/API/DocumentFragment
- MDN DomStringList: https://developer.mozilla.org/en/docs/Web/API/DOMStringList
- MDN DomStringList:https://developer.mozilla.org/en/docs/Web/API/DOMStringList
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
感谢Tim建议使用DocumentFragment,以及abligh在Firefox中捕获错误,原因是使用DOMStringList而不是用于剪贴板的字符串。
#3
101
Simple version : (jQuery)
简单的版本:(jQuery)
$(document).on('paste','[contenteditable]',function(e) {
e.preventDefault();
var text = (e.originalEvent || e).clipboardData.getData('text/plain');
window.document.execCommand('insertText', false, text);
});
Using clipboardData
使用clipboardData
Demo : http://jsbin.com/vokovividu/edit?js,output
演示:http://jsbin.com/vokovividu/edit?js,输出
IE Edge, Firefox, Chrome, Safari, Opera tested.
IE Edge, Firefox, Chrome, Safari, Opera测试。
#4
24
现场演示
Tested on Chrome / FF / IE11
在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
有一个Chrome/IE的烦恼,就是这些浏览器为每个新行添加了
Select some highlighted HTML and paste it here:
function onPaste(e){
var content;
e.preventDefault();
if( e.clipboardData ){
content = e.clipboardData.getData('text/plain');
document.execCommand('insertText', false, content);
return false;
}
else if( window.clipboardData ){
content = window.clipboardData.getData('Text');
if (window.getSelection)
window.getSelection().getRangeAt(0).insertNode( document.createTextNode(content) );
}
}
/////// EVENT BINDING /////////
document.querySelector('[contenteditable]').addEventListener('paste', onPaste);
[contenteditable]{
/* chroem bug: https://*.com/a/24689420/104380 */
display:inline-block;
width: calc(100% - 40px);
min-height:120px;
margin:10px;
padding:10px;
border:1px dashed green;
}
/*
mark HTML inside the "contenteditable"
(Shouldn't be any OFC!)'
*/
[contenteditable] *{
background-color:red;
}
<div contenteditable></div>
#5
15
I've written a little proof of concept for Tim Downs proposal here with off-screen textarea. And here goes the code:
我已经写了一个关于Tim Downs的概念的小证明,在这里用off-screen textarea。代码如下:
<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.
只需将整个代码复制并粘贴到一个html文件中,并尝试在文档的任何地方粘贴(使用ctrl-v)文本。
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.
我已经在IE9和Firefox、Chrome和Opera的新版本中进行了测试。工作的很好。而且,一个人可以使用他喜欢的任何关键组合来测试这个功能。当然,不要忘记包含jQuery源代码。
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).
请随意使用此代码,如果您有一些改进或问题,请将它们发布回来。还要注意,我不是Javascript开发人员,所以我可能遗漏了一些东西(=>做自己的研究)。
#6
11
Based on l2aelba anwser. This was tested on FF, Safari, Chrome, IE (8,9,10 and 11)
基于l2aelba回答。这是在FF、Safari、Chrome(8、9、10和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);
}
}
});
#7
9
This one does not use any setTimeout().
这个不使用任何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
此代码在粘贴前用选择句柄扩展:demo。
#8
5
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:
JS:
function handlepaste(el, e) {
document.execCommand('insertText', false, e.clipboardData.getData('text/plain'));
e.preventDefault();
}
#9
5
This should work on all browsers that support the onpaste event and the mutation observer.
这应该适用于支持onpaste事件和突变观察者的所有浏览器。
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+)
它的工作原理是使用contenteditable、onpaste事件(由所有主要浏览器支持)en突变观察者(由Chrome、Firefox和IE11+支持)
step 1
步骤1
Create a HTML-element with contenteditable
使用contenteditable创建一个html元素。
<div contenteditable="true" id="target_paste_element"></div>
step 2
步骤2
In your Javascript code add the following event
在Javascript代码中添加以下事件。
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.
我们需要将pasteCallBack绑定在一起,因为突变观测者将被异步调用。
step 3
步骤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:
什么代码:
- Somebody fires the paste event by using ctrl-v, contextmenu or other means
- 有人使用ctrl-v、contextmenu或其他方法来触发粘贴事件。
- In the paste event a new element with contenteditable is created (an element with contenteditable has elevated privileges)
- 在paste事件中,创建了一个包含contenteditable的新元素(contenteditable的元素具有更高的特权)
- The caret position of the target element is saved.
- 保存目标元素的caret位置。
- The focus is set to the new element
- 将焦点设置为新元素。
- The content gets pasted into the new element and is rendered in the DOM.
- 内容被粘贴到新元素中并在DOM中呈现。
- The mutation observer catches this (it registers all changes to the dom tree and content). Then fires the mutation event.
- 突变观察者捕捉到这一点(它向dom树和内容注册所有更改)。然后触发突变事件。
- The dom of the pasted content gets cloned into a variable and returned to the callback. The temporary element is destroyed.
- 粘贴内容的dom被克隆到一个变量中并返回回调。临时元素被销毁。
- 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.
- 回调接收克隆的DOM。脱字符号恢复。您可以在将其添加到目标之前进行编辑。元素。在本例中,我正在使用Tim Downs函数来保存/恢复插入符号,并将HTML粘贴到元素中。
Many thanks to Tim Down See this post for the answer:
非常感谢Tim Down,看到这个帖子的答案:
Get the pasted content on document on paste event
在粘贴事件上获取粘贴内容。
#10
4
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);
}
#11
4
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.
这对于评论Nico的答案来说太长了,我认为它对Firefox没有任何作用(根据评论),在Safari上也不适合我。
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
.
因为Firefox有一个类型字段,它是一个不实现测试的DOMStringList。
Next Firefox will not allow paste unless the focus is in a contenteditable=true
field.
下一个Firefox将不允许粘贴,除非焦点位于contenteditable=true字段。
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:
最后,Firefox将不允许可靠地粘贴,除非焦点是在textarea(或者输入)中,这不仅是contenteditable=true,而且:
- 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.
我试图隐藏文本字段,这样我就可以在一个JS VNC模拟器上做粘贴工作(也就是说,它是去一个远程客户端,实际上并没有textarea等)。我发现,试图隐藏上面的文本字段时,有时会出现问题,但在第二次粘贴(或者当字段被清除以防止粘贴相同的数据两次)时,通常会失败,因为字段失去了焦点,并且在focus()的情况下无法正确地重新获得它。我提出的解决方案是把它放在z -1000,让它显示:none,使它为1px×1px,并将所有颜色设置为透明。讨厌的东西。
On Safari, you the second part of the above applies, i.e. you need to have a textarea
which is not display:none
.
在Safari上,你的第二部分是适用的,也就是说,你需要一个没有显示的textarea:none。
#12
3
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
首先想到的是谷歌关闭库的pastehandler, http://closurelibrary.googlecode.com/svn/trunk/closure/goog/demos/pastehandler.html。
#13
3
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);" />
#14
2
This solution is replace the html tag, it's simple and cross-browser; check this jsfiddle: http://jsfiddle.net/tomwan/cbp1u2cx/1/, core code:
这个解决方案是替换html标签,它是简单的和跨浏览器的;检查这个jsfiddle: 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, '');
}
notice: you should do some work about xss filter on the back side because this solution cannot filter strings like '<<>>'
注意:您应该在后面做一些关于xss过滤器的工作,因为这个解决方案不能过滤字符串,比如<<>>'
#15
1
You can do this in this way:
你可以这样做:
use this jQuery plugin for pre & post paste events:
使用这个jQuery插件进行预粘贴事件:
$.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.
首先为所有现有元素设置uid作为数据属性。
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.
然后比较所有节点粘贴事件。因此,通过比较您可以识别新插入的对象,因为它们将有一个uid,然后从新创建的元素中删除样式/类/id属性,这样您就可以保留旧的格式。
#16
1
$('#dom').on('paste',function (e){
setTimeout(function(){
console.log(e.currentTarget.value);
},0);
});
#17
1
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...
让浏览器在其内容可编辑div中像往常一样粘贴,然后在粘贴后将使用自定义文本样式的任何span元素替换为文本本身。这在ie浏览器和我尝试过的其他浏览器中都很正常。
$('[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.
此解决方案假设您正在运行jQuery,并且您不希望在任何内容可编辑div中使用文本格式。
The plus side is that it's super simple.
好的一面是它非常简单。
#18
1
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;
#19
1
Simple solution:
简单的解决方案:
document.onpaste = function(e) {
var pasted = e.clipboardData.getData('Text');
console.log(pasted)
}
#20
0
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
这是一个现有的代码,但是我已经更新了IE的,bug是当现有的文本被选中和粘贴时不会删除所选的内容。这是由下面的代码修复的。
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);
}
});
#1
128
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.
自编写这个答案以来,情况发生了变化:现在Firefox在第22版中添加了支持,所有主流浏览器现在都支持在粘贴事件中访问剪贴板数据。请参见Nico Burns的回答。
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).
在过去,这在跨浏览器的方式中是不可能的。理想的方法是通过粘贴事件获得粘贴内容,这在最近的浏览器中是可能的,但在一些老的浏览器中是不可能的(特别是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:
当您需要支持旧的浏览器时,您所能做的是相当复杂的,并且有一些可以在Firefox 2+、IE 5.5+和WebKit浏览器(如Safari或Chrome)中工作的黑客。TinyMCE和CKEditor的最新版本使用了这种技术:
- Detect a ctrl-v / shift-ins event using a keypress event handler
- 使用keypress事件处理程序检测ctrl-v / shift-ins事件。
- In that handler, save the current user selection, add a textarea element off-screen (say at left -1000px) to the document, turn
designMode
off and callfocus()
on the textarea, thus moving the caret and effectively redirecting the paste - 在该处理程序中,保存当前的用户选择,在文档中添加一个textarea元素(比如左-1000px),然后将designMode关闭,并在textarea上调用focus(),从而移动caret并有效地重定向粘贴。
- Set a very brief timer (say 1 millisecond) in the event handler to call another function that stores the textarea value, removes the textarea from the document, turns
designMode
back on, restores the user selection and pastes the text in. - 在事件处理程序中设置一个非常简短的计时器(比如1毫秒),以调用另一个存储textarea值的函数,从文档中移除textarea,将designMode返回,重新存储用户选择并粘贴文本。
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).
注意,这只适用于键盘粘贴事件,而不是来自上下文或编辑菜单的粘贴。当粘贴事件触发时,将插入符号重定向到textarea(至少在某些浏览器中)已经太晚了。
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.
在您需要支持Firefox 2的不太可能的事件中,请注意,您需要将textarea放置在父文档中,而不是在浏览器中放置WYSIWYG编辑器iframe的文档。
#2
245
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)
适用于IE6+、FF 22+、Chrome、Safari、Edge(只在IE9+中测试过,但应该适用于较低版本)
If you need support for pasting HTML or Firefox <= 22, see Solution #2.
如果您需要支持粘贴HTML或Firefox <= 22,请参见解决方案#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/
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.
注意,这个解决方案使用了getData函数的参数“Text”,这是非标准的。但是,它在所有的浏览器中都是在编写时工作的。
Solution #2 (HTML and works for Firefox <= 22)
Tested in IE6+, FF 3.5+, Chrome, Safari, Edge
测试在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/
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.
div的onpaste事件具有与它相连的handlePaste函数并传递一个参数:粘贴事件的事件对象。我们特别感兴趣的是这个事件的clipboardData属性,它允许在非ie浏览器中访问剪贴板。在IE中相当于窗口。clipboardData,尽管它有一个稍微不同的API。
See resources section below.
请参阅下面的参考资料部分。
The handlepaste
function:
handlepaste函数:
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+.
第一个检查是否存在事件。clipboardData并检查它的类型属性是否包含“文本/html”(类型可能是一个DOMStringList,它是使用包含方法检查的,或者是使用indexOf方法检查的字符串)。如果所有这些条件都满足了,那么我们就像在解决方案#1中一样,除了“text/html”而不是“text/plain”。目前在Chrome和Firefox 22+中工作。
If this method is not supported (all other browsers), then we
如果不支持此方法(所有其他浏览器),那么我们。
- Save the element's contents to a
DocumentFragment
- 将元素的内容保存到DocumentFragment。
- Empty the element
- 空元素
- Call the
waitForPastedData
function - 调用waitForPastedData函数
The waitforpastedata
function:
waitforpastedata函数:
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:
该函数首先对粘贴数据(每20ms一次)进行投票,这是必要的,因为它不会马上出现。当数据出现时:
- Saves the innerHTML of the editable div (which is now the pasted data) to a variable
- 将可编辑div(现在是粘贴数据)的innerHTML保存到一个变量中。
- Restores the content saved in the DocumentFragment
- 重新存储在DocumentFragment中保存的内容。
- Calls the 'processPaste' function with the retrieved data
- 用检索到的数据调用“processPaste”函数。
The processpaste
function:
processpaste函数:
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.
在实际情况中,您可能希望保存之前的选择,并在之后恢复它(在contentEditable上设置游标位置
Resources:
- MDN paste event: https://developer.mozilla.org/en-US/docs/Web/Events/paste
- MDN粘贴事件:https://developer.mozilla.org/en-US/docs/Web/Events/paste
- MSDN clipboard: https://msdn.microsoft.com/en-us/library/ms535220(v=vs.85).aspx
- MSDN剪贴板:https://msdn.microsoft.com/en-us/library/ms535220(v = vs.85). aspx
- MDN DocumentFragment: https://developer.mozilla.org/en/docs/Web/API/DocumentFragment
- MDN DocumentFragment:https://developer.mozilla.org/en/docs/Web/API/DocumentFragment
- MDN DomStringList: https://developer.mozilla.org/en/docs/Web/API/DOMStringList
- MDN DomStringList:https://developer.mozilla.org/en/docs/Web/API/DOMStringList
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
感谢Tim建议使用DocumentFragment,以及abligh在Firefox中捕获错误,原因是使用DOMStringList而不是用于剪贴板的字符串。
#3
101
Simple version : (jQuery)
简单的版本:(jQuery)
$(document).on('paste','[contenteditable]',function(e) {
e.preventDefault();
var text = (e.originalEvent || e).clipboardData.getData('text/plain');
window.document.execCommand('insertText', false, text);
});
Using clipboardData
使用clipboardData
Demo : http://jsbin.com/vokovividu/edit?js,output
演示:http://jsbin.com/vokovividu/edit?js,输出
IE Edge, Firefox, Chrome, Safari, Opera tested.
IE Edge, Firefox, Chrome, Safari, Opera测试。
#4
24
现场演示
Tested on Chrome / FF / IE11
在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
有一个Chrome/IE的烦恼,就是这些浏览器为每个新行添加了
Select some highlighted HTML and paste it here:
function onPaste(e){
var content;
e.preventDefault();
if( e.clipboardData ){
content = e.clipboardData.getData('text/plain');
document.execCommand('insertText', false, content);
return false;
}
else if( window.clipboardData ){
content = window.clipboardData.getData('Text');
if (window.getSelection)
window.getSelection().getRangeAt(0).insertNode( document.createTextNode(content) );
}
}
/////// EVENT BINDING /////////
document.querySelector('[contenteditable]').addEventListener('paste', onPaste);
[contenteditable]{
/* chroem bug: https://*.com/a/24689420/104380 */
display:inline-block;
width: calc(100% - 40px);
min-height:120px;
margin:10px;
padding:10px;
border:1px dashed green;
}
/*
mark HTML inside the "contenteditable"
(Shouldn't be any OFC!)'
*/
[contenteditable] *{
background-color:red;
}
<div contenteditable></div>
#5
15
I've written a little proof of concept for Tim Downs proposal here with off-screen textarea. And here goes the code:
我已经写了一个关于Tim Downs的概念的小证明,在这里用off-screen textarea。代码如下:
<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.
只需将整个代码复制并粘贴到一个html文件中,并尝试在文档的任何地方粘贴(使用ctrl-v)文本。
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.
我已经在IE9和Firefox、Chrome和Opera的新版本中进行了测试。工作的很好。而且,一个人可以使用他喜欢的任何关键组合来测试这个功能。当然,不要忘记包含jQuery源代码。
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).
请随意使用此代码,如果您有一些改进或问题,请将它们发布回来。还要注意,我不是Javascript开发人员,所以我可能遗漏了一些东西(=>做自己的研究)。
#6
11
Based on l2aelba anwser. This was tested on FF, Safari, Chrome, IE (8,9,10 and 11)
基于l2aelba回答。这是在FF、Safari、Chrome(8、9、10和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);
}
}
});
#7
9
This one does not use any setTimeout().
这个不使用任何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
此代码在粘贴前用选择句柄扩展:demo。
#8
5
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:
JS:
function handlepaste(el, e) {
document.execCommand('insertText', false, e.clipboardData.getData('text/plain'));
e.preventDefault();
}
#9
5
This should work on all browsers that support the onpaste event and the mutation observer.
这应该适用于支持onpaste事件和突变观察者的所有浏览器。
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+)
它的工作原理是使用contenteditable、onpaste事件(由所有主要浏览器支持)en突变观察者(由Chrome、Firefox和IE11+支持)
step 1
步骤1
Create a HTML-element with contenteditable
使用contenteditable创建一个html元素。
<div contenteditable="true" id="target_paste_element"></div>
step 2
步骤2
In your Javascript code add the following event
在Javascript代码中添加以下事件。
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.
我们需要将pasteCallBack绑定在一起,因为突变观测者将被异步调用。
step 3
步骤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:
什么代码:
- Somebody fires the paste event by using ctrl-v, contextmenu or other means
- 有人使用ctrl-v、contextmenu或其他方法来触发粘贴事件。
- In the paste event a new element with contenteditable is created (an element with contenteditable has elevated privileges)
- 在paste事件中,创建了一个包含contenteditable的新元素(contenteditable的元素具有更高的特权)
- The caret position of the target element is saved.
- 保存目标元素的caret位置。
- The focus is set to the new element
- 将焦点设置为新元素。
- The content gets pasted into the new element and is rendered in the DOM.
- 内容被粘贴到新元素中并在DOM中呈现。
- The mutation observer catches this (it registers all changes to the dom tree and content). Then fires the mutation event.
- 突变观察者捕捉到这一点(它向dom树和内容注册所有更改)。然后触发突变事件。
- The dom of the pasted content gets cloned into a variable and returned to the callback. The temporary element is destroyed.
- 粘贴内容的dom被克隆到一个变量中并返回回调。临时元素被销毁。
- 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.
- 回调接收克隆的DOM。脱字符号恢复。您可以在将其添加到目标之前进行编辑。元素。在本例中,我正在使用Tim Downs函数来保存/恢复插入符号,并将HTML粘贴到元素中。
Many thanks to Tim Down See this post for the answer:
非常感谢Tim Down,看到这个帖子的答案:
Get the pasted content on document on paste event
在粘贴事件上获取粘贴内容。
#10
4
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);
}
#11
4
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.
这对于评论Nico的答案来说太长了,我认为它对Firefox没有任何作用(根据评论),在Safari上也不适合我。
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
.
因为Firefox有一个类型字段,它是一个不实现测试的DOMStringList。
Next Firefox will not allow paste unless the focus is in a contenteditable=true
field.
下一个Firefox将不允许粘贴,除非焦点位于contenteditable=true字段。
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:
最后,Firefox将不允许可靠地粘贴,除非焦点是在textarea(或者输入)中,这不仅是contenteditable=true,而且:
- 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.
我试图隐藏文本字段,这样我就可以在一个JS VNC模拟器上做粘贴工作(也就是说,它是去一个远程客户端,实际上并没有textarea等)。我发现,试图隐藏上面的文本字段时,有时会出现问题,但在第二次粘贴(或者当字段被清除以防止粘贴相同的数据两次)时,通常会失败,因为字段失去了焦点,并且在focus()的情况下无法正确地重新获得它。我提出的解决方案是把它放在z -1000,让它显示:none,使它为1px×1px,并将所有颜色设置为透明。讨厌的东西。
On Safari, you the second part of the above applies, i.e. you need to have a textarea
which is not display:none
.
在Safari上,你的第二部分是适用的,也就是说,你需要一个没有显示的textarea:none。
#12
3
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
首先想到的是谷歌关闭库的pastehandler, http://closurelibrary.googlecode.com/svn/trunk/closure/goog/demos/pastehandler.html。
#13
3
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);" />
#14
2
This solution is replace the html tag, it's simple and cross-browser; check this jsfiddle: http://jsfiddle.net/tomwan/cbp1u2cx/1/, core code:
这个解决方案是替换html标签,它是简单的和跨浏览器的;检查这个jsfiddle: 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, '');
}
notice: you should do some work about xss filter on the back side because this solution cannot filter strings like '<<>>'
注意:您应该在后面做一些关于xss过滤器的工作,因为这个解决方案不能过滤字符串,比如<<>>'
#15
1
You can do this in this way:
你可以这样做:
use this jQuery plugin for pre & post paste events:
使用这个jQuery插件进行预粘贴事件:
$.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.
首先为所有现有元素设置uid作为数据属性。
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.
然后比较所有节点粘贴事件。因此,通过比较您可以识别新插入的对象,因为它们将有一个uid,然后从新创建的元素中删除样式/类/id属性,这样您就可以保留旧的格式。
#16
1
$('#dom').on('paste',function (e){
setTimeout(function(){
console.log(e.currentTarget.value);
},0);
});
#17
1
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...
让浏览器在其内容可编辑div中像往常一样粘贴,然后在粘贴后将使用自定义文本样式的任何span元素替换为文本本身。这在ie浏览器和我尝试过的其他浏览器中都很正常。
$('[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.
此解决方案假设您正在运行jQuery,并且您不希望在任何内容可编辑div中使用文本格式。
The plus side is that it's super simple.
好的一面是它非常简单。
#18
1
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;
#19
1
Simple solution:
简单的解决方案:
document.onpaste = function(e) {
var pasted = e.clipboardData.getData('Text');
console.log(pasted)
}
#20
0
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
这是一个现有的代码,但是我已经更新了IE的,bug是当现有的文本被选中和粘贴时不会删除所选的内容。这是由下面的代码修复的。
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);
}
});