JavaScript获取剪贴板数据粘贴事件(跨浏览器)

web应用程序如何检测粘贴事件并检索要粘贴的数据?

我想在文本粘贴到富文本编辑器之前删除HTML内容。

在粘贴后清理文本是有效的,但问题是所有以前的格式都会丢失。例如,我可以在编辑器中编写一个句子并将其加粗,但当我粘贴新文本时,所有格式都会丢失。我只想清除粘贴的文本,并保留以前的任何格式不变。

理想情况下,解决方案应该可以跨所有现代浏览器(例如,MSIE、Gecko、Chrome和Safari)工作。

注意,MSIE有clipboardData.getData(),但我找不到其他浏览器的类似功能。

452432 次浏览
首先想到的是谷歌的闭包库的pastehandler http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/pastehandler.html < / p >

写完这篇文章后,情况发生了变化:现在Firefox在版本22中添加了支持,所有主流浏览器现在都支持在粘贴事件中访问剪贴板数据。参见Nico Burns的回答为例。

在过去,这在跨浏览器的方式中通常是不可能的。理想的情况是能够通过paste事件,在最近的浏览器中是可能的获取粘贴的内容,但在一些较旧的浏览器中(特别是Firefox <22).

当你需要支持旧的浏览器时,你可以做的是相当复杂的和一点hack,可以在Firefox 2+, IE 5.5+和WebKit浏览器(如Safari或Chrome)中工作。TinyMCE和CKEditor的最新版本都使用了这种技术:

  1. 使用按键事件处理程序检测ctrl-v / shift-ins事件
  2. 在该处理程序中,保存当前用户选择,向文档添加屏幕外的textarea元素(比如在左边-1000px),关闭designMode并在textarea上调用focus(),从而移动插入符号并有效地重定向粘贴
  3. 在事件处理程序中设置一个非常简短的计时器(比如1毫秒)来调用另一个函数,该函数存储textarea值,从文档中删除textarea,重新打开designMode,恢复用户选择并粘贴文本。

请注意,这将只适用于键盘粘贴事件,而不是粘贴上下文或编辑菜单。当粘贴事件触发时,将插入符号重定向到文本区域就太晚了(至少在某些浏览器中是这样)。

在不太可能的情况下,您需要支持Firefox 2,请注意您需要将文本区域放置在父文档中,而不是在该浏览器中的所见即所得编辑器iframe的文档中。

解决方案#1(纯文本,需要Firefox 22+)

适用于IE6+, FF 22+, Chrome, Safari, Edge (仅在IE9+中测试,但应该适用于较低版本)

如果您需要支持粘贴HTML或Firefox <= 22,请参阅解决方案#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.


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

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

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 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.

Resources on MDN

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

这招对我很管用:

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);" />

我写了一个小的概念证明蒂姆唐斯提议这里与屏幕文本区域。下面是代码:

<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>

只需复制并粘贴整个代码到一个html文件,并尝试粘贴(使用ctrl-v)文本从剪贴板的任何地方的文档。

我在IE9和新版本的Firefox、Chrome和Opera中测试了它。工作得很好。另外,用户可以使用任何他喜欢的组合键来触发这个功能。当然,不要忘记包含jQuery源代码。

请随意使用此代码,如果您有一些改进或问题,请将它们发布回来。还要注意,我不是Javascript开发人员,所以我可能错过了一些东西(=>做你自己的测试)。

这个没有使用任何setTimeout()。

我已经使用伟大的文章来实现跨浏览器支持。

$(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);
}
});
这段代码在粘贴前扩展了选择句柄: 演示 < / p >

对于清除粘贴的文本将当前选定的文本替换为粘贴的文本来说,问题非常简单:

<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();
}

简单的版本:

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

使用 clipboardData

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

Edge, Firefox, Chrome, Safari, Opera测试。

Document.execCommand() is obsolete now。


注意:还记得检查服务器端处的输入/输出(如PHP去掉标记)

你可以这样做:

使用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);
});
});
};

现在你可以使用这个插件;:

$('#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();

解释

首先将所有现有元素的uid设置为data属性。

然后比较所有节点的POST PASTE事件。因此,通过比较,您可以识别新插入的元素,因为它们将有uid,然后只需从新创建的元素中删除style/class/id属性,以便您可以保留旧的格式。

基于l2aelba的答案。这是在FF, Safari, Chrome, IE(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);
}
}
});
解决方案,为我是添加事件监听器粘贴事件,如果你是粘贴到一个文本输入。 由于粘贴事件发生在输入中的文本更改之前,在我的粘贴处理程序中,我创建了一个延迟函数,其中我检查在我的输入框中发生的粘贴更改:

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

这应该适用于所有支持onpaste事件和突变观察者的浏览器。

这个解决方案不仅仅只是获取文本,它实际上允许您在粘贴到元素之前编辑粘贴的内容。

它通过使用contentteditable, onpaste事件(所有主要浏览器支持)在突变观察者(Chrome, Firefox和IE11+支持)中工作。

步骤1

创建一个带有contentteditable的html元素

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

步骤2

在Javascript代码中添加以下事件

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

我们需要绑定pasteCallBack,因为突变观察者将被异步调用。

步骤3

向代码中添加以下函数

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();
}
}
}

代码的作用:

  1. 有人使用ctrl-v, contextmenu或其他方法触发粘贴事件
  2. 在粘贴事件中,创建了一个具有contenteditable的新元素(具有contenteditable的元素具有更高的权限)
  3. 目标元素的插入符号位置被保存。
  4. 焦点被设置为新元素
  5. 内容被粘贴到新元素中,并在DOM中呈现。
  6. 突变观察者捕捉到这一点(它将所有更改注册到dom树和内容)。然后触发突变事件。
  7. 粘贴内容的dom被克隆到一个变量中并返回给回调函数。临时元素被销毁。
  8. 回调接收克隆的DOM。插入符号被恢复。在将其附加到目标之前,可以对其进行编辑。元素。在这个例子中,我使用Tim Downs函数保存/恢复插入符号,并将HTML粘贴到元素中。

例子

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




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;


//edit the copied content by slicing
pasteHtmlAtCaret(this.innerHTML.slice(3), 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();
}
}
}
div {
border: 1px solid black;
height: 50px;
padding: 5px;
}
<div contenteditable="true" id="target_paste_element"></div>


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

Get the pasted content on document on paste event

只需让浏览器像往常一样在其内容可编辑的div中粘贴,然后在粘贴后将用于自定义文本样式的任何span元素与文本本身交换。这似乎工作得很好,在internet explorer和其他浏览器我尝试…

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

这个解决方案假设你正在运行jQuery您不希望在任何内容可编辑div中设置文本格式. c。

好处是它超级简单。

这个解决方案是替换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, '');
}

注意:你应该在后面做一些关于XSS过滤器的工作,因为这个解决方案不能过滤像'<<>>'这样的字符串

Live Demo .

在Chrome / FF / IE11上测试

Chrome/IE有一个恼人的问题,就是这些浏览器会为每一行添加<div>元素。有一篇关于这个在这里的文章,可以通过将contenteditable元素设置为display:inline-block来修复

选择一些突出显示的HTML并粘贴在这里:

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://stackoverflow.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>

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;

这对于Nico的回答来说太长了,我认为它在Firefox上已经不起作用了(根据评论),在Safari上也不起作用了。

首先,您现在似乎可以直接从剪贴板读取。而不是像这样的代码:

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

使用:

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

因为Firefox有一个types字段,它是一个DOMStringList,它没有实现test

接下来,Firefox将不允许粘贴,除非焦点在contenteditable=true字段中。

最后,Firefox将不允许粘贴可靠地,除非焦点在textarea(或者可能是输入)中,这不仅是contenteditable=true,而且:

  • 不是display:none
  • 不是visibility:hidden
  • 不是零大小

我试图隐藏文本字段,这样我就可以在JS VNC模拟器上粘贴工作(即它将到远程客户端,实际上没有textarea等来粘贴)。我发现试图隐藏上面的文本字段会出现一些症状,有时会工作,但通常会在第二次粘贴时失败(或当字段被清除以防止两次粘贴相同的数据时),因为字段失去了焦点,尽管focus(),但不会正确地恢复它。我想出的解决方案是把它放在z-order: -1000,使它display:none,使它为1px * 1px,并将所有颜色设置为透明。讨厌的东西。

在Safari上,上面的第二部分也适用,即你需要有一个不是display:nonetextarea

这是上面发布的一个现有代码,但我已经为IE更新了它,错误是当现有的文本被选择和粘贴时,不会删除所选的内容。下面的代码已经修复了这个问题

selRange.deleteContents();

参见下面的完整代码

$('[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);
}
});

简单的解决方案:

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

粘贴事件是在用户发起“粘贴”时触发的。通过浏览器的用户界面进行操作。

超文本标记语言

<div class="source" contenteditable="true">Try copying text from this box...</div>
<div class="target" contenteditable="true">...and pasting it into this one</div>

JavaScript

const target = document.querySelector('div.target');


target.addEventListener('paste', (event) => {
let paste = (event.clipboardData || window.clipboardData).getData('text');
paste = paste.toUpperCase();


const selection = window.getSelection();
if (!selection.rangeCount) return false;
selection.deleteFromDocument();
selection.getRangeAt(0).insertNode(document.createTextNode(paste));


event.preventDefault();
});

知道更多

为了在IE11和Chrome上都支持纯文本的复制和粘贴,我使用了以下函数。

它有两个if语句来区分IE和chome,并执行适当的代码。在第一部分中,代码从剪贴板中读取文本,在第二部分中,它将文本粘贴到光标位置,替换当前所选文本。

特别是,对于IE上的粘贴,代码获取选择范围,删除所选文本,将文本从剪贴板插入到一个新的html文本节点中,重新配置范围以在光标位置加上文本长度处插入文本节点。

代码如下:

editable.addEventListener("paste", function(e) {
e.preventDefault();


// Get text from the clipboard.
var text = '';
if (e.clipboardData || (e.originalEvent && e.originalEvent.clipboardData)) {
text = (e.originalEvent || e).clipboardData.getData('text/plain');
} else if (window.clipboardData) {
text = window.clipboardData.getData('Text');
}
    

// bool to indicate if the user agent is internet explorer
let isIE = /Trident/.test(navigator.userAgent);
    

if (document.queryCommandSupported('insertText') && !isIE) {
// Chrome etc.
document.execCommand('insertText', false, text);
        

} else {
// IE.
        

// delete content from selection.
var sel = window.getSelection();
var range = sel.getRangeAt(0);
document.execCommand("delete");


// paste plain text in a new node.
var newNode = document.createTextNode(text);


range.insertNode(newNode);
range.setStart(newNode, 0)
range.setEnd(newNode, newNode.childNodes.length);


sel.removeAllRanges;
sel.addRange(range);
}


}, false);

特别是,为了在IE上粘贴文本,我发现这个指令document.execCommand('paste', false, text);在IE11上不起作用,因为浏览器会一次又一次地调用粘贴事件。我用range对象上的函数替换了它。

另一个问题是,在IE11上,根据版本,函数document.execCommand('insertText', false, text);有时可用,有时不可用,所以我显式检查浏览器是否是IE,并为它执行基于范围选择的部分代码(参见else)。