26

It may be duplicate question but i didnt find the solution for this.

I am trying to copy text on button click. Its working on chrome, mozilla(working on on windows and mac but not on linux). And its not working on safari.

I am using document.execCommand("copy") command for copy.

Is safari support this command?

Is there any way which will supports to all browsers?

Akshay Deshmukh
  • 1,242
  • 1
  • 16
  • 31
  • check this question: [js copy to clipboard](http://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript) – Kevin Kloet Oct 20 '16 at 07:16

7 Answers7

27

Please check my solution.

It works on Safari (tested on iPhone 7 and iPad) and on other browsers.

window.Clipboard = (function(window, document, navigator) {
    var textArea,
        copy;

    function isOS() {
        return navigator.userAgent.match(/ipad|iphone/i);
    }

    function createTextArea(text) {
        textArea = document.createElement('textArea');
        textArea.value = text;
        document.body.appendChild(textArea);
    }

    function selectText() {
        var range,
            selection;

        if (isOS()) {
            range = document.createRange();
            range.selectNodeContents(textArea);
            selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
            textArea.setSelectionRange(0, 999999);
        } else {
            textArea.select();
        }
    }

    function copyToClipboard() {        
        document.execCommand('copy');
        document.body.removeChild(textArea);
    }

    copy = function(text) {
        createTextArea(text);
        selectText();
        copyToClipboard();
    };

    return {
        copy: copy
    };
})(window, document, navigator);

// How to use
Clipboard.copy('text to be copied');

https://gist.github.com/rproenca/64781c6a1329b48a455b645d361a9aa3 https://fiddle.jshell.net/k9ejqmqt/1/

Hope that helps you.

Regards.

Rodrigo
  • 639
  • 8
  • 11
  • Got it! Updated to add the code. Thanks for the feedback. – Rodrigo Oct 23 '17 at 11:37
  • 1
    Thanks, this is useful. I had to tweak it a bit to fit my requirements. The browser detection code didn't work on a mac (easily fixed). Also, on my iPhone, the keyboard pops up when this code is invoked. There's a simple way around that as well (return focus to the element that triggered the copy). – Mmm Aug 22 '18 at 21:27
  • can you activate fiddle for this? – Cristal Apr 11 '19 at 10:16
  • Mentioned demo link is in an inactive state. Can you please Activate it? – Mohanraj Periyannan Mar 05 '21 at 09:47
7

now after 4 years of this question,

safari added Clipboard API !

you can write & read texts and any arbitrary data to clipboard on safari (in iOS from v13.4 & desktop v13.1 onwards) .

MDN docs about Clipboard : The Clipboard interface implements the Clipboard API, providing—if the user grants permission—both read and write access to the contents of the system clipboard. The Clipboard API can be used to implement cut, copy, and paste features within a web application.

This is how you can achieve what you want:

navigator.clipboard.writeText("YOUR_TEXT").then(function() {

  /* clipboard successfully set */

}, function() {

  /* clipboard write failed */

});
mohammadavood
  • 101
  • 2
  • 6
  • 3
    I think they finally realized that some developers may need this feature. – Mahdi May 19 '20 at 13:53
  • i just try now on ios 14.4.2 (iPhone 8) so it works well, also according to [caniuse.com](https://caniuse.com/mdn-api_clipboard_writetext) & [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText) it is supported by safari 14 @HoangMinh – mohammadavood May 08 '21 at 06:53
  • you may be tested it on a non-secure protocol ( neither HTTPS nor localhost), or your clipboard permission is blocked in the safari setting @HoangMinh – mohammadavood May 08 '21 at 07:03
  • I have spent the whole day on this issue. Basically in my case, the value that I need to copy is returned from a backend, so I trigger the clipboard.writeText inside my observable and it does not work. I think for some reasons, Apple restricts you to do that. I can use this function to copy as long as I have the value ready, but I cannot put this inside an observable. It works on other browsers (Chrome, Edge, Firefox), but Safari just refuses to copy it – Hoang Minh May 08 '21 at 07:06
  • @HoangMinh I'm facing the same issue like you said, by any chance did you find any solution to it? – kishore Dec 19 '21 at 18:59
  • 2
    @kishore: Sorry for my late response. It's one of the restrictions that Apple put in. Value will not be copied without user interaction. There is no way around that. Basically, you need to follow their rules. What we did was that have a UI input that will show the value that you wan to copy, and the copy button next to it. Value will be copied when the button is clicked. – Hoang Minh Dec 23 '21 at 18:58
  • 2
    Safari 15.4 not copy the text to clipboard with your code snipped. Possible Apple remove this feature ? – TheAlphaGhost May 13 '22 at 13:09
7

Recently I stumbled upon same issue and found out following:

document.execCommand("copy") works without any issue now in Safari.

If you have a specific use case of copy command not working only in safari, one of the things that you may want to check is if your copy command is being run inside an API callback or in some other similar asynchronous fashion. Also, copying in Safari will only work if it is coming from DOM event (console testing won't work).

For me, my text to be copied was coming from async call's response. I had to move API call outside of onClick to prefetch the text and then, only do the copying of that text when copy button is clicked. Worked!

Following code will work without any issue in Safari (given its directly written to DOM event handler like onClick):

var clipBoardElem = document.createElement("input");
document.body.appendChild(clipBoardElem);
clipBoardElem.value = "text";
clipBoardElem.select();
var successfulCopy = document.execCommand('copy');

Once done, you can remove temp elem:

document.body.removeChild(clipBoardElem)
Shyam Kansagra
  • 892
  • 12
  • 24
5

Because the first answer doesn't work for me on iPhone 10 Safari I have tried to find an other solution and I found one described here

Basically it says a very similar solution but different syntax:

supported by "IE 10+, Chrome 43+, Firefox 41+, and Opera 29+"

var copyEmailBtn = document.querySelector('.js-emailcopybtn');
copyEmailBtn.addEventListener('click', function(event) {
  // Select the email link anchor text  
  var emailLink = document.querySelector('.js-emaillink');
  var range = document.createRange();
  range.selectNode(emailLink);
  window.getSelection().addRange(range);

  try {
    // Now that we've selected the anchor text, execute the copy command  
    var successful = document.execCommand('copy');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Copy email command was ' + msg);
  } catch (err) {
    console.log('Oops, unable to copy');
  }

  // Remove the selections - NOTE: Should use
  // removeRange(range) when it is supported  
  window.getSelection().removeAllRanges();
});
body {
  padding: 10px;
}
<p>Email me at <a class="js-emaillink" href="mailto:chriscoyier@gmail.com">chriscoyier@gmail.com</a></p>

<p><button class="js-emailcopybtn">Copy Email</button></p>

It also mentions a lib called clipboardjs what just looks great.

In my case this simple js code works on:

  • iPhone 10 Safari
  • chrome (android / pc (67.0.3396.79))
  • opera (pc (53.0.2907.68))

Unfortunately it doesn't work:

  • pc / android Firefox (60.0.1 (64-bit))

In case of Firefox the simple select and copy does the trick.

element.select();
document.execCommand('copy');
StMa
  • 149
  • 2
  • 5
5

I found that for safari the text needs to be selected before document.execCommand() works.

Also, addRange() is not supported for other browsers (deprecated in Chrome), which means there are instances that it doesn't properly merge the selection and the range together. What this means for user experience was the user would need to click twice in Safari for the value to be copied. Adding .removeAllRanges() before adding the range will help make sure you are grabbing the right selection for the copy. Unclear if you still need the second .removeAllRanges() but I kept it to be safe.

 copy(id) {
    var configId = document.querySelector(id);
    var range = document.createRange();
    range.selectNode(configId);
    var selection = window.getSelection()
    selection.removeAllRanges();
    selection.addRange(range);

    try {
      var successful = document.execCommand('copy');
      var msg = successful ? 'successful' : 'unsuccessful';
      console.log('Copy command was ' + msg);
    } catch (err) {
      console.log('Oops, unable to copy');
    }

    selection.removeAllRanges();
  }

To use (within same class):

<Button icon="copy" onClick={() => {this.copy(id)}}/>

id could be any html selector

This has worked for me in Chrome and Safari.

dwen
  • 141
  • 2
  • 7
  • Is this in the context of React.js? – Brimby Aug 27 '20 at 23:44
  • @Brimby The "To use" is within a React Button, but the copy function is plain JS. – dwen Sep 09 '20 at 18:33
  • I think for the purpose of this answer you should either reformat the button as plain html with a vanilla js listener, or write "Here it is called by a react.js component" so that people who don't know a lot about js or react won't get confused. – Brimby Sep 09 '20 at 23:34
0

According to CanIUse, Safari on iOS doesn't support document.execCommand('copy'), probably because of security reasons.

Mahi
  • 1,707
  • 11
  • 22
  • 1
    So what is the best way for `copy to clipboard` for all browser or how we can do this in safari? – Akshay Deshmukh Oct 20 '16 at 07:10
  • 1
    In case you didn't know @Mahi clipboardjs is actually implemented using `document.execCommand('copy')`. Checkout the sourcecode. – Munim Dec 10 '17 at 16:17
  • document.execCommand('copy') does work on safari as of version 10 – Jair Reina Feb 26 '20 at 21:28
  • Is there any alternative for Safari? We have an internal content management app where we've setup some html templates for marketing and they all use Mac's with Safari... It's a fight to get them to use Chrome. – jeffcodes Mar 09 '20 at 14:03
0

I had the same problem - it turned out my issue was caused because I was creating a temporary select element to copy the text from, which is fine, but I was also hiding it via various methods, the culprit being element.style.width = 0. Removing that and using other methods to hide it solved my issue.

Hope this helps anyone running into the same problem.

mrseanbaines
  • 823
  • 2
  • 12
  • 25