javascript+browser copy to clipboard fails with da

2019-07-21 09:02发布

问题:

Copy to clipboard using document.execCommand('copy') (a la https://stackoverflow.com/a/30810322/3160967) works fine with small amounts of data, and also works if you step through with a debugger, but fails when data size hits about 150k (and you don't step through with a debugger).

Furthermore, it seems to work if the code starting with window.getSelection() is run asynchronously. Why? And is the async solution stable, or is it just masking but not solving the problem?

Using Chrome version 66.0.3359.139

To reproduce this problem, open DevTools and run the following:

function doclick() { 
  copyTest(); 
}

document.addEventListener('click', doclick);

function copyTest() {
    var data = [];
    var n = 20000; // works if n is smaller e.g. < ~6000

    for(var i = 0; i < n; i++)
      data[i] = String(Math.random());

    var textarea = document.createElement('textarea');
    textarea.value = data.join('\t')

    document.body.appendChild(textarea);

    var sel = window.getSelection(); // works if debugger breaks here or earlier
    sel.removeAllRanges();

    var r = document.createRange();
    r.selectNode(textarea);
    sel.addRange(r);

    document.execCommand('copy');
    document.body.removeChild(textarea);
    alert('copied ' + textarea.value.length);
}

After I run this in DevTools, I click on the page to run doCopy(), but the clipboard is unchanged. However, if n < 6000 or so, it will succeed. In addition, it will work if I set a breakpoint on the indicated line above and then continue to run the code after it breaks.

Furthermore, the below seems to work:

function copyTest() {
    var data = [];
    var n = 20000; // works only if n is smaller e.g. < ~6000

    for(var i = 0; i < n; i++)
      data[i] = String(Math.random());

    var textarea = document.createElement('textarea');
    textarea.value = data.join('\t')

    document.body.appendChild(textarea);

    function copy() {
        var sel = window.getSelection(); // works if debugger breaks here or earlier
        sel.removeAllRanges();

        var r = document.createRange();
        r.selectNode(textarea);
        sel.addRange(r);

        document.execCommand('copy');
        document.body.removeChild(textarea);
        alert('copied ' + textarea.value.length);
    }
    window.setTimeout(copy); // this works
}

However, I'm not sure why this should work. It appears that when window.getSelection is called immediately after document.body.appendChild, when the child being appended has a sufficient amount of data, window.getSelection fails, perhaps because document.body.appendChild needs another async cycle to complete? In any case, what is a "proper" solution to this issue-- in pure JS?