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

JavaScript在粘贴事件中获取剪贴板数据(跨浏览器),javascript,cross-browser,clipboard,Javascript,Cross Browser,Clipboard,web应用程序如何检测粘贴事件并检索要粘贴的数据 我想在将文本粘贴到富文本编辑器之前删除HTML内容 在粘贴文本之后清理文本是可行的,但问题是所有以前的格式都会丢失。例如,我可以在编辑器中编写一个句子并将其加粗,但当我粘贴新文本时,所有格式都会丢失。我只想清理粘贴的文本,保留任何以前的格式不变 理想情况下,该解决方案应该适用于所有现代浏览器(如MSIE、Gecko、Chrome和Safari) 请注意,MSIE有clipboardData.getData(),但我在其他浏览器中找不到类似的功能。

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

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

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

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


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

首先想到的是谷歌闭包库的粘贴处理程序
写下这个答案后,情况发生了变化:现在Firefox在版本22中增加了支持,所有主要浏览器现在都支持在粘贴事件中访问剪贴板数据。请参阅以获取示例。

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

当您需要支持较旧的浏览器时,您所能做的是非常复杂的,并且需要一些技巧,这些技巧将适用于Firefox2+、IE 5.5+和WebKit浏览器,如Safari或Chrome。TinyMCE和CKEditor的最新版本都使用此技术:

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

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

    解决方案#1(仅纯文本,需要Firefox 22+) 适用于IE6+、FF 22+、Chrome、Safari和Edge (仅在IE9+中测试,但应适用于较低版本)

    如果您需要粘贴HTML或Firefox的支持(0){ //通过innerHTML检索粘贴的内容 //(或者在此处通过elem.childNodes或elem.getElementsByTagName循环) var pastedData=elem.innerHTML; //恢复保存的内容 elem.innerHTML=“”; 元素appendChild(savedContent); //回拨 processPaste(元素、粘贴数据); } //否则请等待20毫秒,然后重试 否则{ setTimeout(函数(){ waitForPastedData(元素,保存内容) }, 20); } } 函数processPaste(元素、粘贴数据){ //对收集到的数据做任何事情; 警报(粘贴数据); 元素焦点(); } //现代浏览器。注意:Firefox需要第三个参数这对我来说很有效:

    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);" />
    
    函数onPastem(currentData,maxLen){ //验证粘贴文本的最大长度 var totalCharacterCount=window.clipboardData.getData('Text')。长度; }
    我在这里为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>
    
    
    $(文档).ready(函数()
    {
    var ctrlDown=false;
    var ctrlKey=17,vKey=86,cKey=67;
    $(文档).keydown(函数(e)
    {
    如果(e.keyCode==ctrlKey)ctrlDown=true;
    }).keyup(功能(e)
    {
    如果(e.keyCode==ctrlKey)ctrlDown=false;
    });
    $(“.capture-paste”).keydown(函数(e)
    {
    if(ctrlDown&(e.keyCode==vKey | | e.keyCode==cKey)){
    $(“#区域”).css(“显示”、“块”);
    $(“#面积”).focus();
    }
    });
    $(“.capture-paste”).keyup(函数(e)
    {
    如果(ctrlDown&(e.keyCode==vKey | | e.keyCode==cKey)){
    $(“#面积”).blur();
    //你在这里做卫生检查什么的
    $(“#粘贴输出”).text($(“#区域”).val());
    $(“#面积”).val(“”);
    $(“#面积”).css(“显示”、“无”);
    }
    });
    });
    
    只需将整个代码复制并粘贴到一个html文件中,然后尝试(使用ctrl-v)将剪贴板中的文本粘贴到文档的任意位置

    我已经在IE9和Firefox、Chrome和Opera的新版本中进行了测试。效果很好。另外,一个人可以使用他喜欢的任何组合键来触发这个功能也是很好的。当然,不要忘记包含jQuery源代码


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

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

    简单版:

    document.querySelector('[contenteditable]').addEventListener('paste', (e) => {
        e.preventDefault();
        const text = (e.originalEvent || e).clipboardData.getData('text/plain');
        window.document.execCommand('insertText', false, text);
    });
    
    使用
    剪贴板数据

    演示:

    Edge、Firefox、Chrome、Safari和Opera经过测试

    ⚠ Document.execCommand()现在已被禁用


    注意:记住检查服务器端的输入/输出同样(就像)

    您可以这样做
    <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>
    
    $(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>
    
    function handlepaste(el, e) {
      document.execCommand('insertText', false, e.clipboardData.getData('text/plain'));
      e.preventDefault();
    }
    
    document.querySelector('[contenteditable]').addEventListener('paste', (e) => {
        e.preventDefault();
        const text = (e.originalEvent || e).clipboardData.getData('text/plain');
        window.document.execCommand('insertText', false, text);
    });
    
    $.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();
    
        $("#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);
                }
            }
        });
    
    onPaste: function() {
        var oThis = this;
        setTimeout(function() { // Defer until onPaste() is done
            console.log('paste', oThis.input.value);
            // Manipulate pasted input
        }, 1);
    }
    
    $('#dom').on('paste',function (e){
        setTimeout(function(){
            console.log(e.currentTarget.value);
        },0);
    });
    
    <div contenteditable="true" id="target_paste_element"></div>
    
    document.getElementById("target_paste_element").addEventListener("paste", pasteEventVerifierEditor.bind(window, pasteCallBack), false);
    
    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();
        }
    }
    }
    
    $('[contenteditable]').on('paste', function (e) {
        setTimeout(function () {
            $(e.target).children('span').each(function () {
                $(this).replaceWith($(this).text());
            });
        }, 0);
    });
    
    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, '');
    }
    
    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;
    
    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');
    }
    
    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);
        }        
    });
    
    document.onpaste = function(e) {
        var pasted = e.clipboardData.getData('Text');
        console.log(pasted)
    }