30

I have looked at many posts but could not find a clear current answer to the following two questions as it seems standards and browser support has been constantly changing.

Is it a legal operation according to the standard to change the clipboard with event.clipboardData.setData inside a 'copy' event handler?

TylerH
  • 20,799
  • 66
  • 75
  • 101
kofifus
  • 17,260
  • 17
  • 99
  • 173

5 Answers5

58

Clipboard APIs were indeed in active development as of 2016, but things have stabilized since then:

Using event.clipboardData.setData() is supported

Changing the clipboard with event.clipboardData.setData() inside a 'copy' event handler is allowed by the spec (as long as the event is not synthetic).

Note that you need to prevent the default action in the event handler to prevent your changes from being overwritten by the browser:

document.addEventListener('copy', function(e){
  e.clipboardData.setData('text/plain', 'foo');
  e.preventDefault(); // default behaviour is to copy any selected text
});

To trigger the copy event use execCommand

If you need to trigger the copy event (and not just handle the copy requests made by the user via the browser UI), you must use document.execCommand('copy'). It will only work in certain handlers, such as the click handler:

document.getElementById("copyBtn").onclick = function() {
  document.execCommand('copy');
}

Modern browsers support both methods

https://github.com/garykac/clipboard/blob/master/clipboard.md has a compatibility table for execCommand(cut / copy / paste).

You can test this using the snippet below, please comment with the results.

More resources

Testcase

window.onload = function() {
  document.addEventListener('copy', function(e){
    console.log("copy handler");
    if (document.getElementById("enableHandler").checked) {
      e.clipboardData.setData('text/plain', 'Current time is ' + new Date());
      e.preventDefault(); // default behaviour is to copy any selected text
    }
    // This is just to simplify testing:
    setTimeout(function() {
      var tb = document.getElementById("target");
      tb.value = "";
      tb.focus();
    }, 0);
  });
  document.getElementById("execCopy").onclick = function() {
    document.execCommand('copy'); // only works in click handler or other user-triggered thread
  }
  document.getElementById("synthEvt").onclick = function() {
    var e = new ClipboardEvent("copy", {dataType: "text/plain", data:"bar"});
    document.dispatchEvent(e);
  }
}
<html>
<input id="enableHandler" type="checkbox" checked>
<label for="enableHandler">Run clipboardData.setData('text/plain', ...) in the "copy" handler</label>
<p>Try selecting this text and triggering a copy using</p>
<ul>
    <li><button id="execCopy">document.execCommand('copy')</button> - should work.</li>
    <li><button id="synthEvt">document.dispatchEvent(clipboardEvent)</button> - should NOT work</li>
    <li>with keyboard shortcut - should work</li>
    <li>or from the context menu - should work</li>
</ul>
<p>If the "copy" handler was triggered, the focus will move to the textbox below automatically, so that you can try pasting from clipboard:</p>
<input type="text" id="target" size="80">

Async Clipboard API will provide a simpler way to manage the clipboard

When implemented, navigator.clipboard will let you write code like this:

navigator.clipboard.writeText('Text to be copied')
  .then(() => {
    console.log('Text copied to clipboard');
  })
  .catch(err => {
    // This can happen if the user denies clipboard permissions:
    console.error('Could not copy text: ', err);
  });

Chrome 66 starts shipping a partial implementation, and they've published an article about the new API.

Nickolay
  • 31,095
  • 13
  • 107
  • 185
  • When I copied on ios wkwebview on an iPhone with e.clipboardData.setData('text/plain', 'foo'); e.preventDefault(); // default behaviour is to copy any selected text, when I long hold again, thee paste key doesn't appear. it does appear without e.preventDefault() – Jun Jun 24 '17 at 00:09
  • doesn't seem to work in desktop safari. this error appears in the console: `TypeError: undefined is not an object (evaluating 'navigator.clipboard.writeText')` – Crashalot Oct 30 '19 at 20:23
  • I'm curious, with the `.clipboardData.setData()` is it possible to set both text/plain and text/html carriers? I can seem only to one or the other. – Bernd Wechner Aug 16 '21 at 08:50
  • [Supposed to be possible](https://www.w3.org/TR/clipboard-apis/#override-copy), please post a separate question with details. – Nickolay Aug 16 '21 at 08:52
  • All good. Turns out it's possible. Just calling setData twice, once with "text/plain" then with "text/html" does the trick. Just tried it. Pasting the result into a text editor I see my plain string, pasting it into a HTML ready context (like a Thunderbird new message) I get the html string. – Bernd Wechner Aug 22 '21 at 01:36
  • The execCommand has been depreciated. https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand – Matt Smith Jul 26 '22 at 04:12
  • 1
    That seems like poor documentation: "[officially obsolete/deprecated but there's no alternative. So if you must have rich text support, you have to keep using execCommand()](https://stackoverflow.com/a/70831583/1026)" – Nickolay Jul 26 '22 at 09:05
15

You can also just turn it into a function that calls its own handler and removes it

function copyStringToClipboard (string) {
    function handler (event){
        event.clipboardData.setData('text/plain', string);
        event.preventDefault();
        document.removeEventListener('copy', handler, true);
    }

    document.addEventListener('copy', handler, true);
    document.execCommand('copy');
}
Chad Cache
  • 9,668
  • 3
  • 56
  • 48
  • 2
    Note that `event.clipboardData.setData` only works reliably when triggered from user generated `copy`/`paste`/similar events. Because of this, the above `copyStringToClipboard` will not work in the general case since the `copy` event generated by the call to `document.execCommand` is synthetic. – Sebastian Ärleryd Jun 21 '18 at 10:14
3

Bind the element id with copy event and then get the selected text. You could replace or modify the text. Get the clipboard and set the new text. To get the exact formatting you need to set the type as "text/hmtl". You may also bind it to the document instead of element.

 $(ElementId).bind('copy', function(event) {
    var selectedText = window.getSelection().toString(); 
    selectedText = selectedText.replace(/\u200B/g, "");

    clipboardData = event.clipboardData || window.clipboardData || event.originalEvent.clipboardData;
    clipboardData.setData('text/html', selectedText);

    event.preventDefault();
  });
3

For this, we can use browser API. It's worked for me

 async copyClipboard(string){    
   await navigator.clipboard.writeText(string);
   console.log("Text copied");
}
  • This works much better than event.clipboardData.setData for me as after using that the right click and copy no longer works. – MadMac Jun 13 '23 at 06:43
2

There are several answers here that reference execCommand(). This feature is deprecated.

For modern browser support its best to use writeText() which is part of the Clipboard interface.

A simple example would be:

navigator.clipboard.writeText(value)
    .then(() => {
        console.log(value + " copied to clipboard");
    })
    .catch(err => {
        // This can happen if the user denies clipboard permissions:
        console.error('Could not copy text: ', err);
    });
TylerH
  • 20,799
  • 66
  • 75
  • 101
Matt Smith
  • 2,385
  • 2
  • 15
  • 14
  • 1
    Sure, but if you want to copy something other than text writeText() is useless. There is a write() API as well, but it's not usable in (at least) FF. – kleptog Dec 13 '22 at 15:48