如何在javascript中将大量html内容复制到剪贴板而不超时
我注意到如何在javascript中将大量html内容复制到剪贴板而不超时,javascript,clipboard,Javascript,Clipboard,我注意到document.execCommand('copy')命令在后台运行大约5秒后超时。有没有办法绕过这一限制,或者如果需要更长的时间,是否有退路 这是我一直在使用的页面。例如,我有一个函数“准备”数据(从表格数据生成html),然后有第二个函数将数据复制到剪贴板,并附加一些标记。在大型表上,从用户按下Cmd-C到生成并能够复制html,这通常需要大约10秒钟的时间 此外,我注意到Google Sheets允许复制超过5秒的操作,所以我很好奇他们会怎么做: # still works af
document.execCommand('copy')
命令在后台运行大约5秒后超时。有没有办法绕过这一限制,或者如果需要更长的时间,是否有退路
这是我一直在使用的页面。例如,我有一个函数“准备”数据(从表格数据生成html),然后有第二个函数将数据复制到剪贴板,并附加一些标记。在大型表上,从用户按下Cmd-C到生成并能够复制html,这通常需要大约10秒钟的时间
此外,我注意到Google Sheets允许复制超过5秒的操作,所以我很好奇他们会怎么做:
# still works after 25 seconds!
[Violation] 'copy' handler took 25257ms 2217559571-waffle_js_prod_core.js:337
代码被缩小/模糊,因此很难读取,但下面是上面的文件:
作为参考,复制的数据量约为50MB。请在复制操作上使用约10秒的延迟来模拟此长时间运行的过程
对于赏金,我希望有人能展示一个使用单个Cmd-C的工作示例:
- 是否可以在后台(即异步)进行长时间运行的复制操作,例如使用web worker
- 如果必须同步执行,例如执行复制操作,显示一些进度——例如,复制操作可能在大约每10k行之后发出一个事件
preventDefault
并在后台触发复制事件)
您可以将以下内容用作“html生成”功能应如何工作的模板:
function sleepFor( sleepDuration ){
var now = new Date().getTime();
while(new Date().getTime() < now + sleepDuration){ /* do nothing */ }
}
// note: the data should be copied to a dom element and not a string
// so it can be used on `document.execCommand("copy")`
// but using a string below as its easier to demonstrate
// note, however, that it will give a "range exceeded" error
// on very large strings (when using the string, but ignore that,
// as it won't occur when using the proper dom element
var sall='<html><table>'
var srow='<tr><td ><div style="text-align: right"><span style="color: #060606; ">1</span></div></td><td ><div style="text-align: right"><span style="color: #060606; ">Feb 27, 2018</span></div></td><td ><div style="text-align: right"><span style="color: #060606; ">315965</span></div></td><td ><div style="text-align: left"><span style="color: #060606; ">CA</span></div></td><td ><div style="text-align: left"><span style="color: #060606; ">SDBUY</span></div></td><td ><div style="text-align: right"><span style="color: #060606; ">9.99</span></div></td><td ><div style="text-align: left"><span style="color: #060606; ">CAD</span></div></td><td ><div style="text-align: right"><span style="color: #060606; ">7.88</span></div></td></tr>'
for (i=0; i<1e6; i++) {
sall += srow;
if (i%1e5==0) sleepFor(1000); // simulate a 10 second operation...
if (i==(1e6-1)) console.log('Done')
}
sall += '</table></html>'
// now copy to clipboard
函数sleepFor(睡眠持续时间){
var now=new Date().getTime();
while(new Date().getTime()
函数更新主板(newClip){
navigator.clipboard.writeText(newClip).then(function(){
/*剪贴板已成功设置*/
},函数(){
//超时函数处理程序
/*剪贴板写入失败*/
});
}
老实说,我没有看到相同的行为。(编辑:我会注意到我们使用的是略有不同的复制命令)当我照原样使用HTML生成函数时,我得到了一个内存限制错误。具体来说,在循环中附加行的行处出现“Uncaught RangeError:Invalid string length”
如果我降低你的循环(到I{
函数sleepFor(sleepDuration){
var now=new Date().getTime();
while(new Date().getTime()
我怀疑这是否能解决您的实际问题,但这太多了,无法在评论中输入。不过,我希望无论是什么原因导致我们看到的行为差异都会有所帮助。
根据浏览器的不同,这可能不起作用。在Firefox上,它将不起作用,您将在控制台中看到如下消息:
document.execCommand('cut'/'copy')被拒绝,因为它不是从短时间运行的用户生成的事件处理程序内部调用的。
要启用此用例,您需要请求“clipboardWrite”权限。因此,“clipboardWrite”允许您在用户操作的短期事件处理程序之外写入剪贴板
因此,您的数据准备可能需要多长时间,但对execCommand('copy')
的调用必须在用户生成事件(其处理程序正在运行该事件)后立即执行。
显然,它可能是任何事件处理程序,而不仅仅是复制事件
copyFormatted
执行复制
genHtml
函数同步生成HTML数据
enableCopy
将在允许复制的上下文中创建的函数分配给delegateCopy
,该函数在一秒钟后过期(将null分配给delegateCopy
)
有人提到了使用clipboardData
的可能性,虽然这个界面更具编程性,但它也需要最近的用户交互,这是我关注的问题。当然,使用setData
的优点是不需要解析HTML,也不需要为复制的数据创建DOM,在示例中就是这样是大量的数据,而且是实验性的
以下截图显示了一个解决方案:(1)异步运行,(2)在必要时请求用户交互,(3)在可能的情况下使用setData,(3)如果setData
不可用,则使用innerHTML->select.copy方法
//此函数需要一个HTML字符串,并将其复制为富文本。
// https://stackoverflow.com/a/34192073/12750353
函数copyFormatted(html){
//为HTML创建容器
console.log('copyFormatted')
变量c
function onCopyCut(e) {
if (!belongsToInput(e) || signalDOMEvent(cm, e))
return;
if (cm.somethingSelected()) {
setLastCopied({
lineWise: false,
text: cm.getSelections()
});
if (e.type == "cut")
cm.replaceSelection("", null, "cut")
} else if (!cm.options.lineWiseCopyCut)
return;
else {
var ranges = copyableRanges(cm);
setLastCopied({
lineWise: true,
text: ranges.text
});
if (e.type == "cut")
cm.operation(function() {
cm.setSelections(ranges.ranges, 0, sel_dontScroll);
cm.replaceSelection("", null, "cut")
})
}
if (e.clipboardData) {
e.clipboardData.clearData();
var content = lastCopied.text.join("\n");
e.clipboardData.setData("Text", content);
if (e.clipboardData.getData("Text") == content) {
e.preventDefault();
return
}
}
var kludge = hiddenTextarea(),
te = kludge.firstChild;
cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
te.value = lastCopied.text.join("\n");
var hadFocus = document.activeElement;
selectInput(te);
setTimeout(function() {
cm.display.lineSpace.removeChild(kludge);
hadFocus.focus();
if (hadFocus == div)
input.showPrimarySelection()
}, 50)
}
(function() {
window._docs_chrome_extension_exists = !0;
window._docs_chrome_extension_features_version = 1;
window._docs_chrome_extension_permissions = "alarms clipboardRead clipboardWrite identity power storage unlimitedStorage".split(" ");
}
).call(this);
p.B_a = function(a) {
var b = a.Ge().clipboardData;
if (b && (b = b.getData("text/plain"),
!be(Kf(b)))) {
b = Lm(b);
var c = this.C.getRange(),
d = this.C.getRange();
d.jq() && $fc(this.Fd(), d) == this.getValue().length && (c = this.Fd(),
d = c.childNodes.length,
c = TJ(c, 0 < d && XJ(c.lastChild) ? d - 1 : d));
c.yP(b);
VJ(b, !1);
a.preventDefault()
}
};
p.Z1b = function() {
var a = this.C.getRange();
a && 1 < fec(a).textContent.length && SAc(this)
}
p.bxa = function(a, b) {
this.D = b && b.Ge().clipboardData || null;
this.J = !1;
try {
this.rda();
if (this.D && "paste" == b.type) {
var c = this.D,
d = this.L,
e = {},
f = [];
if (void 0 !== c.items)
for (var h = c.items, k = 0; k < h.length; k++) {
var l = h[k],
n = l.type;
f.push(n);
if (!e[n] && d(n)) {
a: switch (l.kind) {
case "string":
var q = xk(c.getData(l.type));
break a;
case "file":
var t = l.getAsFile();
q = t ? Bnd(t) : null;
break a;
default:
q = null
}
var u = q;
u && (e[n] = u)
}
}
else {
var z = c.types || [];
for (h = 0; h < z.length; h++) {
var E = z[h];
f.push(E);
!e[E] && d(E) && (e[E] = xk(c.getData(E)))
}
k = c.files || [];
for (c = 0; c < k.length; c++) {
u = k[c];
var L = u.type;
f.push(L);
!e[L] && d(L) && (e[L] = Bnd(u))
}
}
this.C = e;
a: {
for (d = 0; d < f.length; d++)
if ("text/html" == f[d]) {
var Q = !0;
break a
}
Q = !1
}
this.H = Q || !And(f)
}
this.F.bxa(a, b);
this.J && b.preventDefault()
} finally {
this.D = null
}
}
//Button being a HTML button element
button.addEventListener("click",function(){
execCommand("copy");
});
//This function is called by a click or CMD/CTRL + C
window.addEventListener("copy",function(e){
e.preventDefault();
e.clipboardData.setData("text/plain", "Hey!");
}