I'm using this function to copy a URL to the clipboard:
function CopyUrl($this){
var querySelector = $this.next().attr("id");
var emailLink = document.querySelector("#"+querySelector);
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', false, null);
var msg = successful ? 'successful' : 'unsuccessful';
if(true){
$this.addClass("copied").html("Copied");
}
} catch(err) {
console.log('Oops, unable to copy');
}
// Remove the selections - NOTE: Should use
// removeRange(range) when it is supported
window.getSelection().removeAllRanges();
}
Everything works fine on desktop browsers, but not on iOS devices, where my function returns successfully, but the data isn't copied to the clipboard at all. What's causing this and how could I solve this problem?
Looks like with the help of selection ranges and some little hack it is possible to directly copy to the clipboard on iOS (>= 10) Safari. I personally tested this on iPhone 5C iOS 10.3.3 and iPhone 8 iOS 11.1. However, there seem to be some restrictions, which are:
<input>
and <textarea>
elements.<form>
, then it must be contenteditable
.readonly
(though you may try, this is not an "official" method documented anywhere).To cover all four of these "requirements", you will have to:
<input>
or <textarea>
element.contenteditable
and readonly
of the element to be able to restore them after copying.contenteditable
to true
and readonly
to false
.contenteditable
and readonly
values.execCommand('copy')
.This will cause the caret of the user's device to move and select all the text in the element you want, and then automatically issue the copy command. The user will see the text being selected and the tool-tip with the options select/copy/paste will be shown.
Now, this looks a little bit complicated and too much of an hassle to just issue a copy command, so I'm not sure this was an intended design choice by Apple, but who knows... in the mean time, this currently works on iOS >= 10.
With this said, polyfills like this one could be used to simplify this action and make it cross-browser compatible (thanks @Toskan for the link in the comments).
Working example
To summarize, the code you'll need looks like this:
function iosCopyToClipboard(el) {
var oldContentEditable = el.contentEditable,
oldReadOnly = el.readOnly,
range = document.createRange();
el.contentEditable = true;
el.readOnly = false;
range.selectNodeContents(el);
var s = window.getSelection();
s.removeAllRanges();
s.addRange(range);
el.setSelectionRange(0, 999999); // A big number, to cover anything that could be inside the element.
el.contentEditable = oldContentEditable;
el.readOnly = oldReadOnly;
document.execCommand('copy');
}
Note that the el
parameter to this function must be an <input>
or a <textarea>
.
On iOS < 10 there are some restrictions for Safari (which actually are security measures) to the Clipboard API:
copy
events only on a valid selection and cut
and paste
only in focused editable fields.document.execCommand()
. Note that "shorcut key" means some clickable (e.g. copy/paste action menu or custom iOS keyboard shortcut) or physical key (e.g. connected bluetooth keyboard).ClipboardEvent
constructor.So (at least as of now) it's not possible to programmatically copy some text/value in the clipboard on an iOS device using Javascript. Only the user can decide whether to copy something.
It is however possible to select something programmatically, so that the user only has to hit the "Copy" tool-tip shown on the selection. This can be achieved with the exact same code as above, just removing the execCommand('copy')
, which is indeed not going to work.