当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《JavaScript在粘贴事件上获取剪贴板数据(跨浏览器)》经验,为你挑选了9个好方法。

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

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

之后粘贴后清理文本有效,但问题是所有以前的格式都丢失了.例如,我可以在编辑器中编写一个句子并将其设为粗体,但是当我粘贴新文本时,所有格式都会丢失.我想只清理粘贴的文本,并保持以前的格式不变.

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

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



1> Nico Burns..:
解决方案#1(仅限纯文本,需要Firefox 22+)

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

如果您需要支持粘贴HTML或Firefox <= 22,请参阅解决方案#2.

HTML

Paste

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/

请注意,此解决方案使用参数"Text"作为getData函数,这是非标准的.但是,它在撰写本文时适用于所有浏览器.


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

经过IE6 +,FF 3.5 +,Chrome,Safari,Edge测试

HTML

Paste

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中相当于window.clipboardData,虽然这有一个稍微不同的API.

请参阅下面的资源部分


handlepaste函数:

这个功能有两个分支.

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

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

    将元素的内容保存到a DocumentFragment

    清空元素

    调用该waitForPastedData函数


waitforpastedata函数:

此函数首先轮询粘贴的数据(每20ms一次),这是必要的,因为它不会立即显示.当数据出现时:

    将可编辑div(现在是粘贴数据)的innerHTML保存到变量中

    恢复DocumentFragment中保存的内容

    使用检索到的数据调用'processPaste'函数


processpaste函数:

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


保存并恢复光标位置

在实际情况下,您可能希望在之前保存选择,然后将其恢复(在contentEditable

上设置光标位置).然后,您可以在用户启动粘贴操作时将粘贴的数据插入光标所在的位置.

资源:

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,并且因为使用DOMStringList而不是clipData.types的字符串而在Firefox中捕获错误.


有趣.我以为我过去曾经尝试过这种方式而且在某些浏览器中没有用过,但我确信你是对的.我肯定更喜欢将现有内容移动到`DocumentFragment`而不是使用`innerHTML`有几个原因:首先,你保留任何现有的事件处理程序; 第二,保存和恢复`innerHTML`不能保证创建前一个DOM的相同副本; 第三,然后您可以将选择保存为"范围",而不必添加标记元素或计算文本偏移(如果您使用`innerHTML`则必须执行此操作).
确实有一个没有内容的闪光(FONC?),如果粘贴内容的处理需要一些时间,这显然会更糟.顺便问一下,为什么在IE中提取"DocumentFragment"是一种痛苦?它与其他浏览器相同,除非您使用Range和`extractContents()`来完成它,在任何情况下都不比其他浏览器更简洁.我已经实现了一个你的技术的例子,使用Rangy在各种浏览器中保持良好和统一:http://jsfiddle.net/bQeWC/4/.

2> Tim Down..:

自写这个答案后情况发生了变化:现在Firefox已经在版本22中添加了支持,现在所有主流浏览器都支持在粘贴事件中访问剪贴板数据.请参阅Nico Burns的答案.

在过去,这通常不可能以跨浏览器的方式进行.理想的情况是能够通过paste事件获取粘贴的内容,这在最近的浏览器中是可能的,但在某些较旧的浏览器中却没有(特别是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重新打开,恢复用户选择并粘贴文本.

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

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


@ResistDesign:我不同意 - 这是一种弥补缺乏合理API的不雅和复杂的方法.最好能够直接从粘贴事件中获取粘贴的内容,这可能[在某些浏览器中可能以有限的方式](http://codebits.glennjones.net/editing/getclipboarddata.htm).
@Samuel:您可以使用`paste`事件检测它,但是通常为时已晚,将粘贴重定向到另一个元素,所以这个hack将无法工作.大多数编辑器的后备是显示一个供用户粘贴的对话框.
关于这个的更多信息:Firefox不允许你将焦点移动到`paste`事件中的另一个元素,但它允许你清除元素的内容(并将其保存到变量中,以便以后可以恢复它) ).如果这个容器是一个`div`(它也可能适用于`iframe`),那么你可以使用普通的dom方法遍历粘贴的内容,或者使用`innerHTML`将其作为字符串.然后,您可以恢复`div`的先前内容,并插入您喜欢的任何内容.哦,你必须使用与上面相同的计时器黑客.我很惊讶TinyMCE没有这样做......
我有一种可怕的感觉,你会问.正如我所说,它非常复杂:我建议查看TinyMCE或CKEditor的来源,因为我没有时间概述所涉及的所有问题.简而言之,`designMode`是`document`的布尔属性,当`true`时整个页面可编辑.WYSIWYG编辑器通常使用带有`designMode`的iframe作为可编辑窗格.保存和恢复用户选择在IE中以一种方式完成,在其他浏览器中以另一种方式完成,就像将内容粘贴到编辑器中一样.你需要在IE中获得一个`TextRange`,在其他浏览器中获得`Range`.
如何捕获右键单击 - >粘贴?

3> l2aelba..:

简单版本:

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测试过.


注意:请记住检查服务器端的输入/输出(如PHP strip-tags)


```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(内容); }```
这适用于IE(啊,甜,相反IE)`window.clipboardData.getData('Text');`
这非常有效,但没有版本的IE允许从事件中访问clipboardData :(很好的解决方案,但这应该更高!
到目前为止找到的最佳跨浏览器答案 只需添加IE的代码及其完美.
将使用什么代码来使用IE?

4> vsync..:

现场演示

在Chrome/FF/IE11上测试过

存在Chrome/IE烦恼,即这些浏览器

为每个新行添加元素.有一个关于这个职位在这里,它可以通过设置固定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: /sf/ask/17360801/ */
  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;
}


5> JanM..:

我已经为Tim Downs提出了一个关于屏幕外文本的概念的一些证据.这里是代码:



 





只需将整个代码复制并粘贴到一个html文件中,然后尝试从文档中的任何位置粘贴(使用ctrl-v)文本.

我已经在IE9以及Firefox,Chrome和Opera的新版本中进行了测试.效果很好.也可以使用他喜欢的任何组合组合来实现这一功能.当然不要忘记包含jQuery源代码.

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


e.ctrlKey或e.metaKey是JavaScript DOM的一部分,而不是jQuery:https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
或许它并不总是91:http://stackoverflow.com/questions/3834175/jquery-key-code-for-command-key无论如何,我很确定jQuery会为你处理所有这些,只需检查e.ctrlKey或e.metaKey我想.
我不认为这适用于右键单击和粘贴.很多人都采用这种方法.

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



7> 小智..:

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



8> Matt Crinkla..:

清除粘贴的文本使用粘贴的文本替换当前选定的文本,问题非常简单:

Paste

JS:

function handlepaste(el, e) {
  document.execCommand('insertText', false, e.clipboardData.getData('text/plain'));
  e.preventDefault();
}



9> Mouser..:

这适用于所有支持onpaste事件和变异观察器的浏览器.

此解决方案仅比获取文本更进一步,它实际上允许您在粘贴到元素之前编辑粘贴的内容.

它的工作原理是使用contenteditable,onpaste事件(由所有主流浏览器支持)和变异观察者(由Chrome,Firefox和IE11 +支持)

步骤1

创建一个具有contenteditable的HTML元素

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

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

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

推荐阅读
小妖694_807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有