gpt4 book ai didi

javascript - 如何在javascript中将大量html内容复制到剪贴板而不会超时

转载 作者:行者123 更新时间:2023-12-03 15:39:51 27 4
gpt4 key购买 nike

我注意到 document.execCommand('copy')在后台运行时,命令在大约 5 秒后超时。有没有办法绕过这个限制,或者如果需要更长的时间,也许可以回退?
这是我用于 Clipboard docs 的页面.例如,我有一个“准备”数据的函数(从表格数据生成 html),然后是第二个函数,它使用一些额外的标记将其复制到剪贴板。在大型表上,从用户按下 Cmd-C 到生成 html 并且能够被复制,这通常可能需要十秒钟。
此外,我注意到 Google 表格允许 复制 超过五秒的操作,所以我很好奇他们会怎么做:

# still works after 25 seconds!
[Violation] 'copy' handler took 25257ms 2217559571-waffle_js_prod_core.js:337
代码被缩小/混淆,因此很难阅读,但这是上面的文件: https://docs.google.com/static/spreadsheets2/client/js/2217559571-waffle_js_prod_core.js .
作为引用,被复制的数据量约为 50MB。请在复制操作上使用约 10 秒的延迟来模拟这个长时间运行的过程。

对于赏金,我希望有人可以展示一个执行单个 Cmd-C 的工作示例:
  • 是否可以在后台进行长时间运行的复制操作(即异步),例如使用网络 worker ?
  • 如果它必须同步完成,一个执行复制操作的示例,显示一些进度 - 例如,复制操作可能在每 10k 行左右后发出一个事件。

  • 它必须生成 html 并且必须只涉及单个 Cmd-C(即使我们使用 preventDefault 并在后台触发复制事件。

    您可以将以下内容用作“html-generation”功能应如何工作的模板:
    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
    如果有助于重现真实的复制事件: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard .

    最佳答案

    这是我发现的:
    在这个脚本中:
    https://docs.google.com/static/spreadsheets2/client/js/1150385833-codemirror.js
    我发现了这个功能:

    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);
    这与他们自己的扩展有关
    新发现 2
    当我粘贴到一个单元格中时,它使用这两个函数:
    脚本: https://docs.google.com/static/spreadsheets2/client/js/1526657789-waffle_js_prod_core.js
    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)
    }
    新发现 3
    当我全选并复制时使用此功能:
    脚本: https://docs.google.com/static/spreadsheets2/client/js/1526657789-waffle_js_prod_core.js
    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
    }
    }
    回答您的评论
    这是 e.clipboardData.setData() 之间的区别和 execCommand("copy") : e.clipboardData.setData()用于操作进入剪贴板的数据。 execCommand("copy")以编程方式调用 CMD/CTRL + C .
    如果您调用 execCommand("copy") ,它只会复制您当前的选择,就像您按下 CMD/CTRL + C 一样.您也可以将此功能与 e.clipboardData.setData() 一起使用:
    //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!");
    }
    新发现 3(可能的答案)
    不要使用 setTimeout用于模拟长文本,因为它会卡住 UI。相反,只需使用大量文本。
    此脚本无需超时即可工作。

    window.addEventListener('copy', function(e) {
    e.preventDefault();

    console.log("Started!");
    //This will throw an error on StackOverflow, but works on my website.
    //Use this to disable it for testing on StackOverflow
    //if (!(navigator.clipboard)) {
    if (navigator.clipboard) {
    document.getElementById("status").innerHTML = 'Copying, do not leave page.';
    document.getElementById("main").style.backgroundColor = '#BB595C';
    tryCopyAsync(e).then(() =>
    document.getElementById("main").style.backgroundColor = '#59BBB7',
    document.getElementById("status").innerHTML = 'Idle... Try copying',
    console.log('Copied!')
    );
    } else {
    console.log('Not async...');
    tryCopy(e);
    console.log('Copied!');
    }
    });

    function tryCopy(e) {
    e.clipboardData.setData("text/html", getText());
    }
    function getText() {
    var html = '';
    var row = '<div></div>';
    for (i = 0; i < 1000000; i++) {
    html += row;
    }
    return html;
    }
    async function tryCopyAsync(e) {
    navigator.clipboard.writeText(await getTextAsync());
    }
    async function getTextAsync() {
    var html = '';
    var row = '<div></div>';
    await waitNextFrame();
    for (i = 0; i < 1000000; i++) {
    html += row;
    }
    await waitNextFrame();
    html = [new ClipboardItem({"text/html": new Blob([html], {type: 'text/html'})})];
    return html;
    }

    //Credit: https://stackoverflow.com/a/66165276/7872728
    function waitNextFrame() {
    return new Promise(postTask);
    }

    function postTask(task) {
    const channel = postTask.channel || new MessageChannel();
    channel.port1.addEventListener("message", () => task(), {
    once: true
    });
    channel.port2.postMessage("");
    channel.port1.start();
    }
    #main{
    width:100%;
    height:100vh;
    background:gray;
    color:white;
    font-weight:bold;
    }
    #status{
    text-align:center;
    padding-top:24px;
    font-size:16pt;
    }
    body{
    padding:0;
    margin:0;
    overflow:hidden;
    }
    <div id='main'>
    <div id='status'>Idle... Try copying</div>
    </div>

    要进行测试,请确保在复制之前单击片段内部。
    完整演示

    window.addEventListener("load", function() {
    window.addEventListener("click", function() {
    hideCopying();
    });
    fallbackCopy = 0;
    if (navigator.permissions && navigator.permissions.query && notUnsupportedBrowser()) {
    navigator.permissions.query({
    name: 'clipboard-write'
    }).then(function(result) {
    if (result.state === 'granted') {
    clipboardAccess = 1;
    } else if (result.state === 'prompt') {
    clipboardAccess = 2;
    } else {
    clipboardAccess = 0;
    }
    });
    } else {
    clipboardAccess = 0;
    }
    window.addEventListener('copy', function(e) {
    if (fallbackCopy === 0) {
    showCopying();
    console.log("Started!");
    if (clipboardAccess > 0) {
    e.preventDefault();
    showCopying();
    tryCopyAsync(e).then(() =>
    hideCopying(),
    console.log('Copied! (Async)')
    );
    } else if (e.clipboardData) {
    e.preventDefault();
    console.log('Not async...');
    try {
    showCopying();
    tryCopy(e);
    console.log('Copied! (Not async)');
    hideCopying();
    } catch (error) {
    console.log(error.message);
    }
    } else {
    console.log('Not async fallback...');
    try {
    tryCopyFallback();
    console.log('Copied! (Fallback)');
    } catch (error) {
    console.log(error.message);
    }
    hideCopying();
    }
    } else {
    fallbackCopy = 0;
    }
    });
    });

    function notUnsupportedBrowser() {
    if (typeof InstallTrigger !== 'undefined') {
    return false;
    } else {
    return true;
    }
    }

    function tryCopyFallback() {
    var copyEl = document.createElement
    var body = document.body;
    var input = document.createElement("textarea");
    var text = getText();
    input.setAttribute('readonly', '');
    input.style.position = 'absolute';
    input.style.top = '-10000px';
    input.style.left = '-10000px';
    input.innerHTML = text;
    body.appendChild(input);
    input.focus();
    input.select();
    fallbackCopy = 1;
    document.execCommand("copy");
    }

    function hideCopying() {
    el("main").style.backgroundColor = '#59BBB7';
    el("status").innerHTML = 'Idle... Try copying';
    }

    function showCopying() {
    el("status").innerHTML = 'Copying, do not leave page.';
    el("main").style.backgroundColor = '#BB595C';
    }

    function el(a) {
    return document.getElementById(a);
    }

    function tryCopy(e) {
    e.clipboardData.setData("text/html", getText());
    e.clipboardData.setData("text/plain", getText());
    }

    function getText() {
    var html = '';
    var row = '<div></div>';
    for (i = 0; i < 1000000; i++) {
    html += row;
    }
    return html;
    }
    async function tryCopyAsync(e) {
    navigator.clipboard.write(await getTextAsync());
    }
    async function getTextAsync() {
    var html = '';
    var row = '<div></div>';
    await waitNextFrame();
    for (i = 0; i < 1000000; i++) {
    html += row;
    }
    await waitNextFrame();
    html = [new ClipboardItem({"text/html": new Blob([html], {type: 'text/html'}),"text/plain": new Blob([html], {type: 'text/plain'})})];
    return html;
    }
    //Credit: https://stackoverflow.com/a/66165276/7872728
    function waitNextFrame() {
    return new Promise(postTask);
    }

    function postTask(task) {
    const channel = postTask.channel || new MessageChannel();
    channel.port1.addEventListener("message", () => task(), {
    once: true
    });
    channel.port2.postMessage("");
    channel.port1.start();
    }
    #main {
    width: 500px;
    height: 200px;
    background: gray;
    background: rgba(0, 0, 0, 0.4);
    color: white;
    font-weight: bold;
    margin-left: calc(50% - 250px);
    margin-top: calc(50vh - 100px);
    border-radius: 12px;
    border: 3px solid #fff;
    border: 3px solid rgba(0, 0, 0, 0.4);
    box-shadow: 5px 5px 50px -15px #000;
    box-shadow: 20px 20px 50px 15px rgba(0, 0, 0, 0.3);
    }

    #status {
    text-align: center;
    line-height: 180px;
    vertical-align: middle;
    font-size: 16pt;
    }

    body {
    background: lightgrey;
    background: linear-gradient(325deg, rgba(81, 158, 155, 1) 0%, rgba(157, 76, 79, 1) 100%);
    font-family: arial;
    height: 100vh;
    padding: 0;
    margin: 0;
    overflow: hidden;
    }

    @media only screen and (max-width: 700px) {
    #main {
    width: 100%;
    height: 100vh;
    border: 0;
    border-radius: 0;
    margin: 0;
    }

    #status {
    line-height: calc(100vh - 20px);
    }
    }
    <!DOCTYPE html>
    <html>
    <head>
    <title>Clipboard Test</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta charset='UTF-8'>
    </head>
    <body>
    <div id='main'>
    <div id='status'>Click the webpage to start.</div>
    </div>
    </body>
    </html>

    演示网页: Here is my DEMO
    有用的链接
  • web.dev/async-clipboard/
  • alligator.io/js/async-clipboard-api/
  • developer.mozilla.org/...
  • googlechrome.github.io/samples/async-clipboard/
  • caniuse.com/?search=clipboard
  • sitepoint.com/clipboard-api/
  • codepen.io alternative clipboard functions
  • w3schools.com (the old way)
  • 关于javascript - 如何在javascript中将大量html内容复制到剪贴板而不会超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66058639/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com