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

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

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

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

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


当前回答

为了在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上粘贴许多答案的文本,我找到了这个说明文档。execCommand('paste', false, text);这在IE11上不起作用,因为浏览器会一次又一次地调用粘贴事件。我用range对象上的函数替换了它。

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

其他回答

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

这段代码扩展了粘贴前的选择句柄: 演示

对于清除粘贴文本和替换当前选择的文本与粘贴文本的问题是相当琐碎的:

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

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

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

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

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

Detect a ctrl-v / shift-ins event using a keypress event handler In that handler, save the current user selection, add a textarea element off-screen (say at left -1000px) to the document, turn designMode off and call focus() on the textarea, thus moving the caret and effectively redirecting the paste Set a very brief timer (say 1 millisecond) in the event handler to call another function that stores the textarea value, removes the textarea from the document, turns designMode back on, restores the user selection and pastes the text in.

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

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

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

use:

types = e.clipboardData.types;
if (((types instanceof DOMStringList) && types.contains("text/plain")) ||
    (/text\/plain/.test(types))) {
    // shouldn't this be writing to elem.value for text/plain anyway?
    elem.innerHTML = e.clipboardData.getData('text/plain');
}

因为Firefox有一个类型字段,它是一个DOMStringList,它不实现测试。

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

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

不显示:没有 隐藏不可见性: 不是零大小

I was trying to hide the text field so I could make paste work over a JS VNC emulator (i.e. it was going to a remote client and there was no actually textarea etc to paste into). I found trying to hide the text field in the above gave symptoms where it worked sometimes, but typically failed on the second paste (or when the field was cleared to prevent pasting the same data twice) as the field lost focus and would not properly regain it despite focus(). The solution I came up with was to put it at z-order: -1000, make it display:none, make it as 1px by 1px, and set all the colours to transparent. Yuck.

在Safari上,你应用上面的第二部分,即你需要有一个不显示的文本区域:none。