Ale*_*lex 281 javascript clipboard cross-browser
Web应用程序如何检测粘贴事件并检索要粘贴的数据?
我想在将文本粘贴到富文本编辑器之前删除HTML内容.
之后粘贴后清理文本有效,但问题是所有以前的格式都丢失了.例如,我可以在编辑器中编写一个句子并将其设为粗体,但是当我粘贴新文本时,所有格式都会丢失.我想只清理粘贴的文本,并保持以前的格式不变.
理想情况下,该解决方案应适用于所有现代浏览器(例如,MSIE,Gecko,Chrome和Safari).
请注意,MSIE有clipboardData.getData(),但我找不到其他浏览器的类似功能.
Nic*_*rns 295
适用于IE6 +,FF 22 +,Chrome,Safari,Edge(仅在IE9 +中测试,但适用于较低版本)
如果您需要支持粘贴HTML或Firefox <= 22,请参阅解决方案#2.
<div id='editableDiv' contenteditable='true'>Paste</div>
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
JSFiddle:https://jsfiddle.net/swL8ftLs/12/
请注意,此解决方案使用参数"Text"作为getData函数,这是非标准的.但是,它在撰写本文时适用于所有浏览器.
经过IE6 +,FF 3.5 +,Chrome,Safari,Edge测试
<div id='div' contenteditable='true'>Paste</div>
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
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+.
如果不支持此方法(所有其他浏览器),那么我们
DocumentFragmentwaitForPastedData函数该waitforpastedata函数:
此函数首先轮询粘贴的数据(每20ms一次),这是必要的,因为它不会立即显示.当数据出现时:
该processpaste函数:
使用粘贴的数据做任意事情.在这种情况下,我们只是提醒数据,你可以做任何你喜欢的事情.您可能希望通过某种数据清理过程运行粘贴的数据.
保存并恢复光标位置
在实际情况下,您可能希望在之前保存选择,然后将其恢复(在contentEditable <div>上设置光标位置).然后,您可以在用户启动粘贴操作时将粘贴的数据插入光标所在的位置.
感谢Tim Down建议使用DocumentFragment,并且因为使用DOMStringList而不是clipData.types的字符串而在Firefox中捕获错误.
Tim*_*own 146
自写这个答案后情况发生了变化:现在Firefox已经在版本22中添加了支持,现在所有主流浏览器都支持在粘贴事件中访问剪贴板数据.请参阅Nico Burns的答案.
在过去,这通常不可能以跨浏览器的方式进行.理想的情况是能够通过paste事件获取粘贴的内容,这在最近的浏览器中是可能的,但在某些较旧的浏览器中却没有(特别是Firefox <22).
当您需要支持旧版浏览器时,您可以做的就是参与其中,并且可以在Firefox 2 +,IE 5.5+和WebKit浏览器(如Safari或Chrome)中使用.TinyMCE和CKEditor的最新版本都使用这种技术:
designMode关闭并调用focus()textarea,从而移动插入符并有效地重定向粘贴designMode重新打开,恢复用户选择并粘贴文本.请注意,这仅适用于键盘粘贴事件,而不适用于上下文或编辑菜单中的粘贴.当粘贴事件触发时,将插入符重定向到textarea(在某些浏览器中,至少)为时已晚.
如果您需要支持Firefox 2,请注意您需要将textarea放在父文档中,而不是将WYSIWYG编辑器iframe的文档放在该浏览器中.
l2a*_*lba 121
简单版本:
document.querySelector('[contenteditable]').addEventListener('paste', (e) => {
e.preventDefault();
const text = (e.originalEvent || e).clipboardData.getData('text/plain');
window.document.execCommand('insertText', false, text);
});
Run Code Online (Sandbox Code Playgroud)
运用 clipboardData
演示: http ://jsbin.com/nozifexasu/edit?js,output
Edge,Firefox,Chrome,Safari,Opera测试过.
注意:请记住检查服务器端的输入/输出(如PHP strip-tags)
vsy*_*ync 25
在Chrome/FF/IE11上测试过
存在Chrome/IE烦恼,即这些浏览器<div>为每个新行添加元素.有一个关于这个职位在这里,它可以通过设置固定CONTENTEDITABLE元素是display:inline-block
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);Run Code Online (Sandbox Code Playgroud)
[contenteditable]{
/* chroem bug: https://stackoverflow.com/a/24689420/104380 */
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;
}Run Code Online (Sandbox Code Playgroud)
<div contenteditable></div>Run Code Online (Sandbox Code Playgroud)
Jan*_*anM 15
我已经为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>
Run Code Online (Sandbox Code Playgroud)
只需将整个代码复制并粘贴到一个html文件中,然后尝试从文档中的任何位置粘贴(使用ctrl-v)文本.
我已经在IE9以及Firefox,Chrome和Opera的新版本中进行了测试.效果很好.也可以使用他喜欢的任何组合组合来实现这一功能.当然不要忘记包含jQuery源代码.
请随意使用此代码,如果您有一些改进或问题,请将其发回.另请注意,我不是Javascript开发人员所以我可能错过了一些东西(=>做自己的测试).
tmo*_*ell 10
基于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);
}
}
});
Run Code Online (Sandbox Code Playgroud)
小智 9
这个不使用任何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);
}
});
Run Code Online (Sandbox Code Playgroud)
在粘贴之前,使用选择句柄扩展此代码: demo
要清除粘贴的文本并使用粘贴的文本替换当前选定的文本,问题非常简单:
<div id='div' contenteditable='true' onpaste='handlepaste(this, event)'>Paste</div>
Run Code Online (Sandbox Code Playgroud)
JS:
function handlepaste(el, e) {
document.execCommand('insertText', false, e.clipboardData.getData('text/plain'));
e.preventDefault();
}
Run Code Online (Sandbox Code Playgroud)
这适用于所有支持onpaste事件和变异观察器的浏览器.
此解决方案仅比获取文本更进一步,它实际上允许您在粘贴到元素之前编辑粘贴的内容.
它的工作原理是使用contenteditable,onpaste事件(由所有主流浏览器支持)和变异观察者(由Chrome,Firefox和IE11 +支持)
步骤1
创建一个具有contenteditable的HTML元素
<div contenteditable="true" id="target_paste_element"></div>
Run Code Online (Sandbox Code Playgroud)
第2步
在您的Javascript代码中添加以下事件
document.getElementById("target_paste_element").addEventListener("paste", pasteEventVerifierEditor.bind(window, pasteCallBack), false);
Run Code Online (Sandbox Code Playgroud)
我们需要绑定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();
}
}
Run Code Online (Sandbox Code Playgroud)
}
代码的作用:
非常感谢Tim Down 看到这篇文章的答案:
对于 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');
}
Run Code Online (Sandbox Code Playgroud)
使用:
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');
}
Run Code Online (Sandbox Code Playgroud)
因为 Firefox 有一个未实现的types字段。DOMStringListtest
接下来,除非焦点位于某个字段中,否则 Firefox 将不允许粘贴contenteditable=true。
最后,Firefox 将不允许可靠地粘贴,除非焦点位于textarea(或者可能是输入)中,这不仅是contenteditable=true:
display:nonevisibility:hidden我试图隐藏文本字段,这样我就可以在 JS VNC 模拟器上进行粘贴工作(即,它将发送到远程客户端,并且实际上没有要textarea粘贴的内容)。我发现尝试隐藏上面的文本字段有时会出现它起作用的症状,但通常在第二次粘贴时失败(或者当清除该字段以防止粘贴相同的数据两次时),因为该字段失去焦点并且无法正确重新获得尽管focus()。我想出的解决方案是将其放在z-order: -1000,使其display:none为 1px x 1px,并将所有颜色设置为透明。恶心。
在 Safari 上,上面的第二部分适用,即您需要有一个textareawhich 不是display:none。
| 归档时间: |
|
| 查看次数: |
330154 次 |
| 最近记录: |