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


299

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

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

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

理想情况下,该解决方案应可在所有现代浏览器(例如MSIE,Gecko,Chrome和Safari)上运行。

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


所有这些答案都说明了如何获取文本内容。获取图像内容或文件内容需要进行更多工作。也许我们可以将标题更改为“ JavaScript获取消毒的文本剪贴板数据...”
1.21吉瓦

1
就像尼克说:event.clipboardData.getData('Text')为我工作。
安德烈·埃里科

document.addEventListener('paste'...为我工作,但如果用户希望能够将其粘贴到页面上的其他位置,则会引起冲突。然后我尝试了myCanvasElement.addEventListener('paste'...,但是那没有用。最终我想通了myCanvasElement.parentElement.addEventListener('paste'...
瑞安

Answers:


149

自编写此答案以来,情况已经发生了变化:由于Firefox已在版本22中添加了支持,因此所有主流浏览器现在都支持在粘贴事件中访问剪贴板数据。有关示例,请参见Nico Burns的答案

过去,这通常不可能以跨浏览器的方式实现。理想的办法是能够通过paste事件获取粘贴的内容,这在最近的浏览器中是可能的,但在某些较旧的浏览器(尤其是Firefox <22)中则不可能

当您需要支持较旧的浏览器时,您可以做的事情很多,并且有些hacker可以在Firefox 2 +,IE 5.5+和WebKit浏览器(例如Safari或Chrome)中使用。TinyMCE和CKEditor的最新版本都使用此技术:

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

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

万一需要支持Firefox 2,请注意,您需要将文本区域放置在该浏览器的父文档中,而不是WYSIWYG编辑器iframe的文档中。


1
哇,谢谢你!不过,这似乎是非常复杂的技巧;-)您能否再介绍一下designMode和选择内容,尤其是在第3步中?非常感谢!
Alex

5
我有一种可怕的感觉,你会问这个。正如我所说,它涉及的非常多:建议您查看TinyMCE或CKEditor的来源,因为我没有时间概述所有涉及的问题。简要地说,designMode是的布尔属性,document当时,整个页面都可编辑true。所见即所得的编辑器通常使用带iframe的iframe designMode作为可编辑窗格。保存和还原用户选择是一种在IE中进行的方式,另一种是在其他浏览器中进行的方式,就像将内容粘贴到编辑器中一样。您需要TextRange在IE和Range其他浏览器中获得一个。
Tim Down'2

6
@Samuel:您可以使用paste事件检测到它,但是到那时将粘贴重定向到另一个元素通常为时已晚,因此此hack将无法正常工作。大多数编辑器的后备功能是显示一个对话框供用户粘贴。
蒂姆·唐

6
有关此问题的更多信息:Firefox不允许您在paste事件中将焦点移到另一个元素,但是它将允许您清除元素的内容(并将其保存到变量中,以便以后可以还原它)。如果此容器是div(可能也适用iframe),则可以使用常规dom方法循环浏览粘贴的内容,或使用来将其作为字符串获取innerHTML。然后,您可以还原的先前内容div,并插入所需的任何内容。哦,您必须使用与上述相同的计时器技巧。我很惊讶TinyMCE没有这样做...
Nico Burns

8
@ResistDesign:我不同意-这是弥补缺乏明智API的一种优雅而复杂的方法。最好能够直接从paste事件获取粘贴的内容,这在某些浏览器中可能是有限的
Tim Down

318

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

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

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

请注意,此解决方案为getData函数使用参数“文本” ,这是非标准的。但是,在撰写本文时,它可在所有浏览器中使用。


解决方案#2(HTML并适用于Firefox <= 22)

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

说明

onpaste事件div具有handlePaste附加的功能,并传递了一个参数:event粘贴事件的对象。我们特别感兴趣的是clipboardData此事件的属性,该属性允许在非ie浏览器中访问剪贴板。在IE中,等效项为window.clipboardData,尽管API略有不同。

请参阅下面的资源部分。


handlepaste函数:

此功能有两个分支。

第一个检查event.clipboardData是否存在,并检查其types属性是否包含“ text / html”(types可以DOMStringList是使用contains方法检查的a ,也可以是使用方法检查的字符串indexOf)。如果所有这些条件都满足,那么我们按照解决方案#1进行,除了使用'text / html'而不是'text / plain'。目前,该功能适用​​于Chrome和Firefox 22+。

如果不支持此方法(所有其他浏览器),那么我们

  1. 将元素的内容保存到 DocumentFragment
  2. 清空元素
  3. 调用waitForPastedData函数

waitforpastedata函数:

此功能首先轮询粘贴的数据(每20毫秒一次),这是必需的,因为它不会立即出现。数据出现后:

  1. 将可编辑div的innerHTML(现在是粘贴的数据)保存到变量中
  2. 恢复保存在DocumentFragment中的内容
  3. 使用检索到的数据调用“ processPaste”函数

processpaste函数:

对粘贴的数据进行任意处理。在这种情况下,我们只是提醒数据,您可以做任何您想做的事情。您可能需要通过某种数据清理过程来运行粘贴的数据。


保存和还原光标位置

在实际情况中,您可能需要先保存选择,然后再保存选择(在contentEditable <div>上设置光标位置)。然后,您可以在用户启动粘贴操作时将粘贴的数据插入光标所在的位置。

资源:

感谢Tim Down建议使用DocumentFragment,并感谢由于使用DOMStringList而不是剪贴板数据的字符串而在Firefox中捕获错误。


4
有趣。我以为我曾经尝试过这种方法,但在某些浏览器中却无法使用,但我确定您是对的。出于以下几个原因,我绝对希望将现有内容移入DocumentFragment而不是使用innerHTML:首先,保留所有现有的事件处理程序。其次,innerHTML不能保证保存和还原可以创建与先前DOM相同的副本。第三,您可以将选择另存为,Range而不必费力添加标记元素或计算文本偏移量(如果使用,则必须这样做innerHTML)。
蒂姆·唐

3
确实有没有内容的闪烁(FONC?),如果粘贴内容的处理需要一些时间,这显然会更糟。顺便说一句,为什么DocumentFragment在IE中痛苦不堪?除非您使用Range并extractContents()执行此操作,否则它与其他浏览器相同,这在任何情况下都不比替代方法更为简洁。我已经实现了一个有关您的技术的示例,使用Rangy在所有浏览器之间保持美观和统一:jsfiddle.net/bQeWC/4
蒂姆·唐

1
@马丁:我在评论中发布的jsFiddle演示可能会有所帮助。
蒂姆·唐

1
似乎在Windows的Firefox 28(至少)上它不再起作用。它永远不会超出waitforpastedata功能范围
Oliboy50 2014年

1
仅供参考:Edge现在支持text/html使用W3C剪贴板API 以MIME类型读取数据。过去,这样的尝试会引发异常。因此,Edge不再需要这种解决方法/ hack。
珍妮·奥雷利

130

简单版本:

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,输出

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

⚠Document.execCommand ()现在已过时


注意:请记住还要在服务器端检查输入/输出(例如PHP strip-tags


4
这确实很好,但是没有IE版本允许通过事件访问剪贴板数据:(很好的解决方案,但是,它应该更高!
Eric Wood

1
看来您可以用IE的另一种方式获取剪贴板数据,因此,如果您检测到IE,则可以使用该数据代替提示回退:msdn.microsoft.com/en-us/library/ie/ms535220(v = vs.85).aspx
Andrew

4
迄今为止找到的最佳跨浏览器答案。只需添加IE及其完美代码即可。
阿图罗

6
这在IE中起作用(啊,甜蜜,与IE相反)window.clipboardData.getData('Text');
Benjineer 2015年

9
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); }
Yukulelix

26

现场演示

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


1
我需要粘贴作为纯文本功能。在IE9和IE10上进行了测试,效果很好。不用说它也可以在主要浏览器上使用...谢谢。
Savas Vedova

2
您的代码包含一个错误:if(e.originalEvent.clipboardData)会导致NPE,因为您当时不知道e.originalEvent是否存在
塞巴斯蒂安

15

我在这里用屏幕外文本区域为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源。

随时使用此代码,如果您有一些改进或问题,请发回。还要注意,我不是Java语言开发人员,所以我可能错过了一些东西(=>做自己的见证)。


Mac不使用ctrl-v粘贴,而是使用cmd-v。因此,将ctrlKey设置为91而不是17
Jeremy T

2
也许并不总是91:stackoverflow.com/questions/3834175/… 无论如何,我很确定jQuery会为您处理所有这些,只需检查我认为的e.ctrlKey或e.metaKey。
杰里米T

3
e.ctrlKey或e.metaKey是JavaScript DOM(而不是jQuery)的一部分:developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
2013年

2
我认为这不适用于右键单击和粘贴。很多人都采用这种方法。
埃里克·伍德

10

基于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时是否可以保留新行?
Staysee 2014年

10

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

粘贴之前,使用选择句柄扩展了此代码: demo


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

5

为了清除粘贴的文本并将当前选定的文本替换为粘贴的文本,这很简单:

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

您可以在此提供演示页面吗?我已经尝试过,但是它不起作用
vsync

5

这应该在所有支持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,因为变异观察者将被异步调用。

第三步

将以下函数添加到您的代码中

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粘贴到元素中。


非常感谢Tim Down 看到这篇文章的答案:

在粘贴事件中获取文档上的粘贴内容


4

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

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

2
不幸的是,恐怖是我们工作描述的一部分;)但是我同意,这是一个hack,只有在其他所有选项都用尽时才应使用hack。
Lex 2014年

4

对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字段。DOMStringListtest

除非焦点位于contenteditable=true字段中,否则下一步Firefox将不允许粘贴。

最后,Firefox将不允许粘贴可靠,除非焦点在一个textarea(或者输入),这不仅是contenteditable=true也:

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

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

在Safari上,您可以使用上述第二部分,即您需要使用一个textareanot display:none


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


3

简单的解决方案:

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

2

这为我工作:

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

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

1

您可以通过以下方式执行此操作:

使用此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属性,即可保留较旧的格式。


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

1

只需让浏览器像往常一样在其内容可编辑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标记?我想这个问题是关于所有标签的。
亚历克西斯·威尔克

1

该解决方案是替换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过滤器是否做得很好无关。黑客还是会绕过您的JS过滤的100%。
亚历克西斯·威尔克

切勿使用Regex解析/转换HTML!
SubliemeSiem

0

这是上面发布的现有代码,但我已经为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);
    }        
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.