Web 应用程序如何检测粘贴事件并检索要粘贴的数据?
我想在将文本粘贴到富文本编辑器之前删除 HTML 内容。
在粘贴之后清理文本是可行的,但问题是所有以前的格式都丢失了。例如,我可以在编辑器中写一个句子并将其加粗,但是当我粘贴新文本时,所有格式都丢失了。我想只清除粘贴的文本,而保留任何以前的格式不变。
理想情况下,该解决方案应该适用于所有现代浏览器(例如 MSIE、Gecko、Chrome 和 Safari)。
请注意,MSIE 有 clipboardData.getData()
,但我找不到其他浏览器的类似功能。
event.clipboardData.getData('Text')
为我工作。
document.addEventListener('paste'...
对我有用,但如果用户希望能够粘贴到页面上的其他位置,则会导致冲突。然后我尝试了 myCanvasElement.addEventListener('paste'...
,但没有奏效。最终我发现 myCanvasElement.parentElement.addEventListener('paste'...
起作用了。
解决方案 #1(仅限纯文本,需要 Firefox 22+)
适用于 IE6+、FF 22+、Chrome、Safari、Edge(仅在 IE9+ 中测试,但应适用于较低版本)
如果您需要支持粘贴 HTML 或 Firefox <= 22,请参阅解决方案 #2。
函数句柄粘贴(e){ var剪贴板数据,粘贴数据; // 停止将数据实际粘贴到 div e.stopPropagation(); e.preventDefault(); // 通过剪贴板 API 获取粘贴的数据 clipboardData = e.clipboardData || window.clipboardData; pastedData = clipboardData.getData('Text'); // 用 pasteddata 做任何事情 alert(pastedData); } document.getElementById('editableDiv').addEventListener('paste', handlePaste);
请注意,此解决方案使用 getData
函数的参数“文本”,这是非标准的。但是,在撰写本文时,它适用于所有浏览器。
解决方案 #2(HTML 并且适用于 Firefox <= 22)
在 IE6+、FF 3.5+、Chrome、Safari、Edge 中测试
var editableDiv = document.getElementById('editableDiv'); function handlepaste(e) { var types, pastedData, savedContent; // 支持剪贴板 API 中的 'text/html' 类型的浏览器(Chrome、Firefox 22+) if (e && e.clipboardData && e.clipboardData.types && e.clipboardData.getData) { // 检查 'text /html' 在类型列表中。有关 // 为什么需要 DOMStringList 位的详细信息,请参见下面的 abligh 的答案。我们不能回退到“文本/纯文本”,因为 // Safari/Edge 不会宣传 HTML 数据,即使它是可用的 types = e.clipboardData.types; if (((types instanceof DOMStringList) && types.contains("text/html")) || (types.indexOf && types.indexOf('text/html') !== -1)) { // 提取数据并将其传递给回调 pasteedData = e.clipboardData.getData('text/html'); processPaste(editableDiv, pastedData); // 停止实际粘贴数据 e.stopPropagation(); e.preventDefault();返回假; } } // 其他所有内容:将现有元素内容移动到 DocumentFragment 以妥善保管 savedContent = document.createDocumentFragment(); while (editableDiv.childNodes.length > 0) { savedContent.appendChild(editableDiv.childNodes[0]); } // 然后等待浏览器粘贴内容并清理 waitForPastedData(editableDiv, savedContent);返回真; } function waitForPastedData(elem, savedContent) { // 如果数据已被浏览器处理,则处理它 if (elem.childNodes && elem.childNodes.length > 0) { // 通过 innerHTML 检索粘贴的内容 // (或者循环遍历 elem .childNodes 或 elem.getElementsByTagName ) var pastedData = elem.innerHTML; // 恢复保存的内容 elem.innerHTML = ""; elem.appendChild(savedContent); // 调用回调 processPaste(elem, pastedData); } // 否则等待 20 毫秒再试一次 else { setTimeout(function() { waitForPastedData(elem, savedContent) }, 20); } } function processPaste(elem, pastedData) { // 对收集到的数据做任何事情;警报(粘贴数据); elem.focus(); } // 现代浏览器。注意:Firefox <= 6 需要第三个参数 if (editableDiv.addEventListener) { editableDiv.addEventListener('paste', handlepaste, false); } // IE <= 8 else { editableDiv.attachEvent('onpaste', handlepaste); }
解释
div
的 onpaste
事件附加了 handlePaste
函数并传递了一个参数:粘贴事件的 event
对象。我们特别感兴趣的是这个事件的 clipboardData
属性,它允许在非 ie 浏览器中访问剪贴板。在 IE 中等效的是 window.clipboardData
,尽管它的 API 略有不同。
请参阅下面的资源部分。
handlepaste
函数:
这个函数有两个分支。
第一个检查 event.clipboardData
是否存在并检查它的 types
属性是否包含“text/html”(types
可能是使用 contains
方法检查的 DOMStringList
,也可能是使用 indexOf
方法检查)。如果满足所有这些条件,那么我们将按照解决方案 #1 进行处理,除了使用“text/html”而不是“text/plain”。这目前适用于 Chrome 和 Firefox 22+。
如果不支持此方法(所有其他浏览器),那么我们
将元素的内容保存到 DocumentFragment 清空元素 调用 waitForPastedData 函数
waitforpastedata
函数:
此函数首先轮询粘贴的数据(每 20 毫秒一次),这是必要的,因为它不会立即出现。当数据出现时:
将可编辑 div 的 innerHTML(现在是粘贴的数据)保存到变量中 恢复 DocumentFragment 中保存的内容 使用检索到的数据调用“processPaste”函数
processpaste
函数:
对粘贴的数据进行任意操作。在这种情况下,我们只是提醒数据,您可以为所欲为。您可能希望通过某种数据清理过程来运行粘贴的数据。
保存和恢复光标位置
在实际情况中,您可能希望先保存选择,然后再恢复它 (Set cursor position on contentEditable <div>)。然后,您可以将粘贴的数据插入到用户启动粘贴操作时光标所在的位置。
MDN 上的资源
粘贴事件
文档片段
DomStringList
感谢 Tim Down 建议使用 DocumentFragment,并感谢由于使用 DOMStringList 而不是 clipboardData.types 的字符串而在 Firefox 中捕获错误
自从写下这个答案后,情况发生了变化:现在 Firefox 在版本 22 中添加了支持,所有主要浏览器现在都支持在粘贴事件中访问剪贴板数据。有关示例,请参见 Nico Burns's answer。
在过去,这通常不可能以跨浏览器的方式实现。理想的情况是能够通过 paste
事件 which is possible in recent browsers 获取粘贴的内容,但不能在某些较旧的浏览器中(特别是 Firefox <22)。
当您需要支持较旧的浏览器时,您可以做的是相当复杂的事情,并且需要一些技巧,这将适用于 Firefox 2+、IE 5.5+ 和 WebKit 浏览器,例如 Safari 或 Chrome。 TinyMCE 和 CKEditor 的最新版本都使用了这种技术:
使用按键事件处理程序检测 ctrl-v / shift-ins 事件在该处理程序中,保存当前用户选择,在文档中添加一个屏幕外的 textarea 元素(比如左侧 -1000px),关闭 designMode 并调用 focus( ) 在 textarea 上,从而移动插入符号并有效地重定向粘贴 在事件处理程序中设置一个非常简短的计时器(例如 1 毫秒)以调用另一个存储 textarea 值的函数,从文档中删除 textarea,重新打开 designMode,恢复用户选择并粘贴文本。
请注意,这仅适用于键盘粘贴事件,而不适用于上下文或编辑菜单中的粘贴。当 paste 事件触发时,将插入符号重定向到 textarea 为时已晚(至少在某些浏览器中)。
万一您需要支持 Firefox 2,请注意您需要将 textarea 放置在父文档中,而不是该浏览器中的 WYSIWYG 编辑器 iframe 的文档中。
designMode
是 document
的布尔属性,并且在 true
时使整个页面可编辑。 WYSIWYG 编辑器通常使用带有 designMode
的 iframe 作为可编辑窗格。保存和恢复用户选择在 IE 中以一种方式完成,在其他浏览器中以另一种方式完成,就像将内容粘贴到编辑器中一样。您需要在 IE 中获取 TextRange
,在其他浏览器中获取 Range
。
paste
事件检测到它,但是到那时将粘贴重定向到另一个元素通常为时已晚,因此此 hack 将不起作用。大多数编辑器的后备是显示一个对话框供用户粘贴。
paste
事件中将焦点移动到另一个元素,但是它允许您清除元素的内容(并将其保存到变量中以便以后恢复它)。如果此容器是 div
(它可能也适用于 iframe
),那么您可以使用普通 dom 方法循环浏览粘贴的内容,或使用 innerHTML
将其作为字符串获取。然后您可以恢复 div
的先前内容,并插入您喜欢的任何内容。哦,你必须使用与上面相同的计时器技巧。我很惊讶 TinyMCE 不这样做......
简单版:
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() 现在是 obsolete。
注意:记得检查服务器端的输入/输出(如PHP strip-tags)
window.clipboardData.getData('Text');
e.preventDefault(); if (e.clipboardData) { content = (e.originalEvent || e).clipboardData.getData('text/plain'); document.execCommand('insertText', false, content); } else if (window.clipboardData) { content = window.clipboardData.getData('Text'); document.selection.createRange().pasteHTML(content); }
在 Chrome / FF / IE11 上测试
有一个 Chrome/IE 烦恼是这些浏览器为每个新行添加 <div>
元素。有一篇关于此 here 的帖子,可以通过将 contenteditable 元素设置为 display:inline-block
来修复它
选择一些突出显示的 HTML 并将其粘贴到此处:
函数 onPaste(e){ var content; e.preventDefault(); if(e.clipboardData){ content = e.clipboardData.getData('text/plain'); document.execCommand('insertText', false, content);返回假; } else if( window.clipboardData ){ content = window.clipboardData.getData('Text'); if (window.getSelection) window.getSelection().getRangeAt(0).insertNode(document.createTextNode(content)); } } /////// 事件绑定 ///////// document.querySelector('[contenteditable]').addEventListener('paste', onPaste); [contenteditable]{ /* chrome 错误:https://stackoverflow.com/a/24689420/104380 */ display:inline-block;宽度:计算(100% - 40px);最小高度:120px;边距:10px;填充:10px;边框:1px 虚线绿色; } /* 在“contenteditable”中标记 HTML(不应该是任何 OFC!)' */ [contenteditable] *{ background-color:red; }
我在这里用屏幕外的文本区域为 Tim Downs 的提案写了一点概念证明。代码如下:
<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 开发人员,所以我可能遗漏了一些东西(=> 做你自己的测试)。
基于 l2aelba anwser。已在 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);
}
}
});
这个不使用任何 setTimeout()。
我使用了this 篇很棒的文章来实现跨浏览器支持。
$(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);
}
});
此代码在粘贴前使用选择句柄进行了扩展:demo
对于清理粘贴的文本并用粘贴的文本替换当前选定的文本,这件事非常简单:
<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();
}
对 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
字段,它是一个没有实现 test
的 DOMStringList
。
Next Firefox 将不允许粘贴,除非焦点位于 contenteditable=true
字段中。
最后,Firefox 将不允许粘贴可靠,除非焦点位于 textarea
(或者可能是输入)中,它不仅是 contenteditable=true
,而且:
不显示:无
不可见性:隐藏
不是零大小
我试图隐藏文本字段,这样我就可以在 JS VNC 模拟器上进行粘贴工作(即,它要发送到远程客户端,实际上没有 textarea
等可以粘贴)。我发现尝试隐藏上面的文本字段有时会出现症状,但通常在第二次粘贴时失败(或者当该字段被清除以防止两次粘贴相同的数据时),因为该字段失去焦点并且无法正确恢复尽管 focus()
。我想出的解决方案是把它放在 z-order: -1000
,使它成为 display:none
,使它成为 1px x 1px,并将所有颜色设置为透明。呸。
在 Safari 上,上述第二部分适用,即您需要有一个不是 display:none
的 textarea
。
这应该适用于支持 onpaste 事件和突变观察者的所有浏览器。
此解决方案比仅获取文本更进一步,它实际上允许您在将粘贴的内容粘贴到元素中之前对其进行编辑。
它通过使用 contenteditable、onpaste 事件(所有主流浏览器都支持)和突变观察者(Chrome、Firefox 和 IE11+ 支持)来工作
步骤1
使用 contenteditable 创建一个 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();
}
}
}
代码的作用:
有人使用 ctrl-v、contextmenu 或其他方式触发粘贴事件 在粘贴事件中,创建了一个具有 contenteditable 的新元素(具有 contenteditable 的元素具有提升的权限) 保存了目标元素的插入符号位置。焦点设置到新元素 内容被粘贴到新元素中并在 DOM 中呈现。突变观察者捕捉到了这一点(它记录了对 dom 树和内容的所有更改)。然后触发突变事件。粘贴内容的 dom 被克隆到一个变量中并返回给回调。临时元素被销毁。回调接收克隆的 DOM。插入符号已恢复。您可以在将其附加到目标之前对其进行编辑。元素。在此示例中,我使用 Tim Downs 函数来保存/恢复插入符号并将 HTML 粘贴到元素中。
例子
document.getElementById("target_paste_element").addEventListener("paste", pasteEventVerifierEditor.bind(window, pasteCallBack), false); function pasteEventVerifierEditor(callback, e) { //在粘贴事件上触发。 //将内容粘贴到另一个可编辑的div中,变异观察者观察到这一点,内容被粘贴,dom树被复制并可以通过回调引用。 //创建临时div //保存插入符号的位置。 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(); //我们必须等待变化发生。 //附加一个变异观察者 if (window['MutationObserver']) { //这是新功能 //观察者存在于firefox/chrome和IE11中 //选择目标节点 //创建一个观察者实例 tempDiv.observer = new MutationObserver(粘贴MutationObserver.bind(窗口,回调)); // 观察者的配置: var config = { attributes: false, childList: true, characterData: true, subtree: true }; // 传入目标节点,以及观察者选项 tempDiv.observer.observe(tempDiv, config); } } 函数 pasteMutationObserver(callback) { document.getElementById("id_tempDiv_paste_editor").observer.disconnect();删除 document.getElementById("id_tempDiv_paste_editor").observer; if (callback) { //将复制的 dom 树返回给提供的回调。 //复制以避免关闭。 callback.apply(document.getElementById("id_tempDiv_paste_editor").cloneNode(true)); } document.body.removeChild(document.getElementById("id_tempDiv_paste_editor")); } function pasteCallBack() { //将内容粘贴到元素中。 restoreSelection(document.getElementById("target_paste_element"), savedCaret);删除已保存的Caret; //通过切片来编辑复制的内容 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;返回 { 开始:开始,结束:开始 + 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],节点,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);发现开始=真; } if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) { range.setEnd(node, savedSel.end - charIndex);停止=真; } 字符索引 = 下一个字符索引; } else { var i = node.childNodes.length; while (i--) { nodeStack.push(node.childNodes[i]); } } } var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(范围); } function pasteHtmlAtCaret(html, returnInNode, selectPastedContent) { //Tim Down 编写的函数 var sel, range; if (window.getSelection) { // IE9 和非 IE sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { range = sel.getRangeAt(0); range.deleteContents(); // Range.createContextualFragment() 在这里很有用,但 // 只是最近才标准化,并且在 // 某些浏览器(IE9,例如)中不受支持 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); // 保留选择 if (lastNode) { range = range.cloneRange(); if (returnInNode) { range.setStart(lastNode, 0); //这部分已编辑,在粘贴的节点内设置插入符号。 } else { range.setStartAfter(lastNode); } if (selectPastedContent) { range.setStartBefore(firstNode); } 其他 { range.collapse(true); } sel.removeAllRanges(); sel.addRange(范围); } } } 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 { 边框:1px 纯黑色;高度:50px;填充:5px; }
非常感谢 Tim Down 请参阅此帖子以获取答案:
Get the pasted content on document on paste event
对我有用的解决方案是在粘贴到文本输入时添加事件侦听器来粘贴事件。由于粘贴事件发生在输入中的文本更改之前,因此在我的粘贴处理程序中我创建了一个延迟函数,在该函数中我检查在粘贴时我的输入框中发生的更改:
onPaste: function() {
var oThis = this;
setTimeout(function() { // Defer until onPaste() is done
console.log('paste', oThis.input.value);
// Manipulate pasted input
}, 1);
}
首先想到的是 google 的闭包库 http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/pastehandler.html 的 pastehandler
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;
简单的解决方案:
document.onpaste = function(e) {
var pasted = e.clipboardData.getData('Text');
console.log(pasted)
}
这对我有用:
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);" />
你可以这样做:
将此 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 作为数据属性。
然后比较所有节点的 POST PASTE 事件。因此,通过比较您可以识别新插入的元素,因为它们将具有 uid,然后只需从新创建的元素中删除 style/class/id 属性,这样您就可以保留旧格式。
$('#dom').on('paste',function (e){
setTimeout(function(){
console.log(e.currentTarget.value);
},0);
});
只需让浏览器像往常一样在其内容可编辑的 div 中粘贴,然后在粘贴后将用于自定义文本样式的任何 span 元素与文本本身交换。这似乎在 Internet Explorer 和我尝试过的其他浏览器中工作正常......
$('[contenteditable]').on('paste', function (e) {
setTimeout(function () {
$(e.target).children('span').each(function () {
$(this).replaceWith($(this).text());
});
}, 0);
});
此解决方案假定您正在运行 jQuery,并且您不希望在任何内容可编辑的 div 中设置文本格式。
好处是它超级简单。
span
标记?我想问题是关于所有标签的。
该解决方案是替换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 过滤器的工作,因为这个解决方案不能过滤像 '<<>>' 这样的字符串
这是上面发布的现有代码,但我已经为 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);
}
});
当用户通过浏览器的用户界面启动“粘贴”操作时,将触发粘贴事件。
HTML
<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 上不起作用,因为浏览器会多次调用粘贴事件。所以我用范围对象上的函数替换了它。
另一个问题是,在 IE11 上,根据版本,函数 document.execCommand('insertText', false, text);
有时可用,有时不可用,所以我明确检查浏览器是否为 IE,并根据范围选择执行部分代码(参见 else )。
DocumentFragment
而不是使用innerHTML
,原因如下:首先,您保留所有现有的事件处理程序;其次,保存和恢复innerHTML
并不能保证创建之前 DOM 的相同副本;第三,您可以将所选内容另存为Range
,而不必费心添加标记元素或计算文本偏移量(如果您使用innerHTML
,则必须这样做)。DocumentFragment
会很痛苦?这与其他浏览器中的相同,除非您使用 Range 和extractContents()
来执行此操作,这在任何情况下都不会比替代方法更简洁。我已经实现了您的技术的一个示例,使用 Rangy 使浏览器之间保持良好和统一:jsfiddle.net/bQeWC/4。waitforpastedata
函数