ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

理想情况下,该解决方案应该适用于所有现代浏览器(例如 MSIE、Gecko、Chrome 和 Safari)。

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

所有这些答案都解释了如何获取文本内容。获取图像内容或文件内容需要更多的工作。也许我们可以将标题更改为“JavaScript 获取已清理的文本剪贴板数据...”
就像 nico 说的:event.clipboardData.getData('Text') 为我工作。
document.addEventListener('paste'... 对我有用,但如果用户希望能够粘贴到页面上的其他位置,则会导致冲突。然后我尝试了 myCanvasElement.addEventListener('paste'...,但没有奏效。最终我发现 myCanvasElement.parentElement.addEventListener('paste'... 起作用了。

C
Cristian Ciupitu

解决方案 #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);

粘贴

JSFiddle

请注意,此解决方案使用 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); }

粘贴

JSFiddle

解释

divonpaste 事件附加了 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 中捕获错误


有趣的。我以为我过去曾尝试过,但它在某些浏览器中不起作用,但我相信你是对的。我肯定更喜欢将现有内容移动到 DocumentFragment 而不是使用 innerHTML,原因如下:首先,您保留所有现有的事件处理程序;其次,保存和恢复 innerHTML 并不能保证创建之前 DOM 的相同副本;第三,您可以将所选内容另存为 Range,而不必费心添加标记元素或计算文本偏移量(如果您使用 innerHTML,则必须这样做)。
确实存在一闪而过的无内容(FONC?),如果处理粘贴的内容需要一些时间,显然会更糟。顺便说一句,为什么在 IE 中提取到 DocumentFragment 会很痛苦?这与其他浏览器中的相同,除非您使用 Range 和 extractContents() 来执行此操作,这在任何情况下都不会比替代方法更简洁。我已经实现了您的技术的一个示例,使用 Rangy 使浏览器之间保持良好和统一:jsfiddle.net/bQeWC/4
@Martin:我在评论中发布的 jsFiddle 演示可能会有所帮助。
似乎它不再适用于 Windows 的 Firefox 28(至少)。它永远不会超出 waitforpastedata 函数
@TSR您可能想使用 stackoverflow.com/questions/2920150/… 之类的东西在光标位置插入一个字符串。
C
Community

自从写下这个答案后,情况发生了变化:现在 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 和 selection 的内容,尤其是在第 3 步中?非常感谢!
我有一种可怕的感觉,你会问这个。正如我所说,它涉及很多:我建议查看 TinyMCE 或 CKEditor 的来源,因为我没有时间概述所有涉及的问题。简而言之,designModedocument 的布尔属性,并且在 true 时使整个页面可编辑。 WYSIWYG 编辑器通常使用带有 designMode 的 iframe 作为可编辑窗格。保存和恢复用户选择在 IE 中以一种方式完成,在其他浏览器中以另一种方式完成,就像将内容粘贴到编辑器中一样。您需要在 IE 中获取 TextRange,在其他浏览器中获取 Range
@Samuel:您可以使用 paste 事件检测到它,但是到那时将粘贴重定向到另一个元素通常为时已晚,因此此 hack 将不起作用。大多数编辑器的后备是显示一个对话框供用户粘贴。
有关此的更多信息:Firefox 不允许您在 paste 事件中将焦点移动到另一个元素,但是它允许您清除元素的内容(并将其保存到变量中以便以后恢复它)。如果此容器是 div(它可能也适用于 iframe),那么您可以使用普通 dom 方法循环浏览粘贴的内容,或使用 innerHTML 将其作为字符串获取。然后您可以恢复 div 的先前内容,并插入您喜欢的任何内容。哦,你必须使用与上面相同的计时器技巧。我很惊讶 TinyMCE 不这样做......
@ResistDesign:我不同意——这是一种不雅且复杂的方式来弥补缺乏合理的 API。最好能直接从粘贴事件中获取粘贴的内容,即possible in a limited way in some browsers
S
SergeyR

简单版:

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


这非常有效,但是没有任何版本的 IE 允许从事件访问剪贴板数据 :( 很好的解决方案,不过,这应该更高!
看起来您可以通过不同的方式在 IE 中获取剪贴板数据,因此如果您检测到 IE,您可以使用该数据而不是提示回退:msdn.microsoft.com/en-us/library/ie/ms535220(v=vs.85).aspx
迄今为止找到的最佳跨浏览器答案。只需添加 IE 的代码及其完美。
这在 IE 中有效(啊,甜蜜,相反的 IE)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); }
v
vsync

Live Demo

在 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; }


我需要粘贴为纯文本功能。在 IE9 和 IE10 上测试,效果很好。不用说,它也适用于主流浏览器......谢谢。
您的代码包含一个错误: if(e.originalEvent.clipboardData) 可能导致 NPE,因为您不知道此时是否存在 e.originalEvent
J
JanM

我在这里用屏幕外的文本区域为 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 开发人员,所以我可能遗漏了一些东西(=> 做你自己的测试)。


Mac 不使用 ctrl-v 粘贴,而是使用 cmd-v。所以设置 ctrlKey = 91 而不是 17
或者它并不总是 91:stackoverflow.com/questions/3834175/… 无论如何,我很确定 jQuery 会为您处理所有这些,只需检查 e.ctrlKey 或 e.metaKey 我认为。
e.ctrlKey 或 e.metaKey 是 JavaScript DOM 的一部分,而不是 jQuery:developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
我认为这不适用于右键单击和粘贴。很多人都采用这种方法。
t
tmorell

基于 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);
            }
        }
    });

粘贴到 IE 时有没有办法保留新行?
A
AsgarAli

这个不使用任何 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


+1 我比 Nico Burns 更喜欢这个,尽管我认为每个人都有自己的位置。
M
Matt Wonlaw

对于清理粘贴的文本并用粘贴的文本替换当前选定的文本,这件事非常简单:

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

你能提供一个演示页面吗?我已经尝试过了,但它不起作用
a
abligh

对 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 字段,它是一个没有实现 testDOMStringList

Next Firefox 将不允许粘贴,除非焦点位于 contenteditable=true 字段中。

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

不显示:无

不可见性:隐藏

不是零大小

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

在 Safari 上,上述第二部分适用,即您需要有一个不是 display:nonetextarea


也许在浏览器渲染引擎上工作的开发人员应该在文档站点上有一个页面或空间,他们可以使用这些页面或空间来写关于他们所从事的功能的注释。例如,如果他们使用粘贴功能,他们会添加,“如果没有显示、隐藏可见性或大小为零,粘贴将不起作用”。
M
Mouser

这应该适用于支持 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


L
Lex

对我有用的解决方案是在粘贴到文本输入时添加事件侦听器来粘贴事件。由于粘贴事件发生在输入中的文本更改之前,因此在我的粘贴处理程序中我创建了一个延迟函数,在该函数中我检查在粘贴时我的输入框中发生的更改:

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

不幸的是,恐怖是我们工作描述的一部分;)但我同意,这是一种技巧,只有在用尽所有其他选项时才应使用技巧。
t
tDo

首先想到的是 google 的闭包库 http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/pastehandler.html 的 pastehandler


这个似乎可以安全地检测到粘贴事件,但似乎无法捕获/返回粘贴的内容?
@Alex:你是对的,这也只适用于文本区域,而不是富文本编辑器。
I
Ivan
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;

k
kungfooman

简单的解决方案:

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

H
HoLyVieR

这对我有用:

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

P
Peeyush

你可以这样做:

将此 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 属性,这样您就可以保留旧格式。


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

D
DaveAlger

只需让浏览器像往常一样在其内容可编辑的 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 标记?我想问题是关于所有标签的。
T
TomWan

该解决方案是替换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 过滤器的工作,因为这个解决方案不能过滤像 '<<>>' 这样的字符串


服务器上的 XSS 归档与您的 JavaScript 过滤器是否做得好无关。无论如何,黑客都会绕过 100% 的 JS 过滤。
永远不要使用正则表达式来解析/转换 HTML!
R
Ravi Selvaraj

这是上面发布的现有代码,但我已经为 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);
    }        
});

M
MD SHAYON

当用户通过浏览器的用户界面启动“粘贴”操作时,将触发粘贴事件。

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

Know more


D
Donato Pirozzi

为了支持在 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 )。