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

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

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

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

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

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


自写这个答案以来,情况发生了变化:既然Firefox已经增加了对版本22的支持,所有主流浏览器现在都支持在粘贴事件中访问剪贴板数据。 以尼科伯恩斯的答案为例。

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

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

  • 使用按键事件处理程序检测ctrl-v / shift-ins事件
  • 在该处理程序中,保存当前的用户选择,在屏幕外添加一个textarea元素(比如左-1000px)到文档中,关闭designMode并调用textarea上的focus() ,从而移动插入符号并有效地重定向粘贴
  • 在事件处理程序中设置一个非常简短的计时器(例如1毫秒),以调用另一个存储textarea值的函数,从文档中移除textarea,重新打开designMode ,恢复用户选择并粘贴文本。
  • 请注意,这只适用于键盘粘贴事件,不适用于上下文或编辑菜单。 在粘贴事件触发时,将脱字符重定向到textarea(至少在某些浏览器中)为时已晚。

    如果您需要支持Firefox 2,请注意,您需要将textarea放在父文档中,而不是所见即所得编辑器iframe的文档在该浏览器中。


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

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

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

    说明

    divonpaste事件附加了handlePaste函数,并传递了一个参数:paste事件的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函数:

    这个函数首先查询粘贴的数据(每20ms一次),这是必要的,因为它不会马上出现。 当数据出现时:

  • 将可编辑的div(现在是粘贴的数据)的innerHTML保存到变量中
  • 恢复保存在DocumentFragment中的内容
  • 用检索到的数据调用'processPaste'函数

  • processpaste功能:

    用粘贴的数据做任意事情。 在这种情况下,我们只是提醒数据,你可以做任何你喜欢的事情。 您可能希望通过某种数据清理过程来运行粘贴的数据。


    保存并恢复光标位置

    在真实情况下,您可能希望先保存选区,然后再进行恢复(在contentEditable <div>上设置光标位置)。 然后,您可以在用户启动粘贴操作时将粘贴的数据插入光标所在的位置。

    资源:

  • MDN粘贴事件:https://developer.mozilla.org/en-US/docs/Web/Events/paste
  • MSDN剪贴板:https://msdn.microsoft.com/en-us/library/ms535220(v=vs.85).aspx
  • MDN DocumentFragment:https://developer.mozilla.org/en/docs/Web/API/DocumentFragment
  • MDN DomStringList:https://developer.mozilla.org/en/docs/Web/API/DOMStringList
  • 感谢Tim Down建议使用DocumentFragment,并且abligh用于捕获Firefox中的错误,因为使用了DOMStringList而不是用于clipboardData.types的字符串


    简单版本: (jQuery)

    $(document).on('paste','[contenteditable]',function(e) {
        e.preventDefault();
        var text = (e.originalEvent || e).clipboardData.getData('text/plain');
        window.document.execCommand('insertText', false, text);
    });
    

    使用 clipboardData

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

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

    链接地址: http://www.djcxy.com/p/61597.html

    上一篇: JavaScript get clipboard data on paste event (Cross browser)

    下一篇: Understanding the options in a handlebars #view helper for Ember.JS