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

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

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

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

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


当前回答

这个解决方案是替换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过滤器的工作,因为这个解决方案不能过滤像'<<>>'这样的字符串

其他回答

基于l2aelba答案。这是在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);
            }
        }
    });

这个解决方案是替换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过滤器的工作,因为这个解决方案不能过滤像'<<>>'这样的字符串

简单的版本:

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测试。

机场当局:Document.execCommand()现已废弃。


注意:记住在服务器端也检查输入/输出(如PHP条带标签)

这招对我很管用:

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

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

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

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

函数handlePaste(e) { var clipboardData,粘贴数据; //停止数据实际粘贴到div e.stopPropagation (); e.preventDefault (); //通过剪贴板API获取粘贴数据 clipboardData = e.clipboardData || window.clipboardData; paste data = clipboardData.getData('Text'); //对粘贴数据做任何事情 警报(pastedData); } . getelementbyid(“editableDiv”)。addEventListener(“粘贴”,handlePaste); <div id='editableDiv' contenteditable='true'>粘贴</div>

JSFiddle

注意,这个解决方案为getData函数使用了参数“Text”,这是非标准的。但是,在编写本文时,它可以在所有浏览器中运行。


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

测试在IE6+, FF 3.5+, Chrome, Safari, Edge

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); } <div id='div' contenteditable='true'>Paste</div>

JSFiddle

解释

div的onpaste事件附加了handlePaste函数,并传递了一个参数:粘贴事件的事件对象。我们特别感兴趣的是这个事件的clipboardData属性,它允许在非ie浏览器中访问剪贴板。在IE中,对应的是window。clipboardData,尽管它有一个稍微不同的API。

请参阅下面的参考资料部分。


句柄粘贴函数:

这个函数有两个分支。

第一个检查事件是否存在。clipboardData和检查它的类型属性是否包含'text/html'(类型可以是使用contains方法检查的DOMStringList,也可以是使用indexOf方法检查的字符串)。如果所有这些条件都满足,那么我们继续执行解决方案#1,只是使用'text/html'而不是'text/plain'。目前Chrome和Firefox 22+都可以使用。

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

将元素的内容保存到一个DocumentFragment中 清空元素 调用waitforpastedata函数


waitforpastedata函数:

这个函数首先轮询粘贴的数据(每20毫秒一次),这是必要的,因为它不会立即出现。当数据出现时:

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


processpaste函数:

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


保存并恢复光标位置

在实际情况下,您可能希望在之前保存选择,然后在之后恢复它(将光标位置设置为contentEditable <div>)。然后,您可以在用户发起粘贴操作时光标所在的位置插入粘贴的数据。

MDN资源

粘贴事件 文档片段 DomStringList

感谢Tim Down建议使用DocumentFragment,以及abligh在Firefox中捕捉由于使用DOMStringList而不是clipboardData.types的字符串而导致的错误