Copy to clipboard using Javascript in iOS
Update! iOS >= 10
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:
- Text can only be copied from
<input>
and<textarea>
elements. - If the element holding the text is not inside a
<form>
, then it must becontenteditable
. - The element holding the text must not be
readonly
(though you may try, this is not an "official" method documented anywhere). - The text inside the element must be in selection range.
To cover all four of these "requirements", you will have to:
- Put the text to be copied inside an
<input>
or<textarea>
element. - Save the old values of
contenteditable
andreadonly
of the element to be able to restore them after copying. - Change
contenteditable
totrue
andreadonly
tofalse
. - Create a range to select the desired element and add it to the window's selection.
- Set the selection range for the entire element.
- Restore the previous
contenteditable
andreadonly
values. - Run
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>
.
Old answer: previous iOS versions
On iOS < 10 there are some restrictions for Safari (which actually are security measures) to the Clipboard API:
- It fires
copy
events only on a valid selection andcut
andpaste
only in focused editable fields. - It only supports OS clipboard reading/writing via shortcut keys, not through
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). - It doesn't support the
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.
How to copy text to clipboard/pasteboard with Swift
If all you want is plain text, you can just use the string
property. It's both readable and writable:
// write to clipboard
UIPasteboard.general.string = "Hello world"
// read from clipboard
let content = UIPasteboard.general.string
(When reading from the clipboard, the UIPasteboard documentation also suggests you might want to first check hasStrings
, "to avoid causing the system to needlessly attempt to fetch data before it is needed or when the data might not be present", such as when using Handoff.)
Copy and Paste on iPhone with multiple data representations
To answer my own question:
You have to used the items property to put multiple representations onto the pasteboard. To do so you create a dictionary with each representation as the value and the representation type as the key. Add this dictionary to an array, where each item in the array represents an item (UIPasteboard supports adding multiple items to the pasteboard as well as adding mutliple representation to each item).
Example code for one single item with two representations:
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSMutableDictionary *item = [NSMutableDictionary dictionaryWithCapacity:2];
[item setValue:[NSKeyedArchiver archivedDataWithRootObject:pasteboardDictionary] forKey:MNTNodesPasteboardType];
[item setValue:pasteboardString forKey:(NSString *)kUTTypeUTF8PlainText];
pasteboard.items = [NSArray arrayWithObject:item];
Don't forget to link with the MobileCoreServices framework to resolve the UTI constant.
Javascript - Copy string to clipboard as text/html
I have done a few modifications on Loilo's answer above:
setting (and later restoring) the focus to the hidden div prevents FF going into endless recursion when copying from a textarea
setting the range to the inner children of the div prevents chrome inserting an extra
<br>
in the beginningremoveAllRanges on getSelection() prevents appending to existing selection (possibly not needed)
try/catch around execCommand
hiding the copy div better
On OSX this will not work. Safari does not support execCommand and chrome OSX has a known bug https://bugs.chromium.org/p/chromium/issues/detail?id=552975
code:
clipboardDiv = document.createElement('div');
clipboardDiv.style.fontSize = '12pt'; // Prevent zooming on iOS
// Reset box model
clipboardDiv.style.border = '0';
clipboardDiv.style.padding = '0';
clipboardDiv.style.margin = '0';
// Move element out of screen
clipboardDiv.style.position = 'fixed';
clipboardDiv.style['right'] = '-9999px';
clipboardDiv.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
// more hiding
clipboardDiv.setAttribute('readonly', '');
clipboardDiv.style.opacity = 0;
clipboardDiv.style.pointerEvents = 'none';
clipboardDiv.style.zIndex = -1;
clipboardDiv.setAttribute('tabindex', '0'); // so it can be focused
clipboardDiv.innerHTML = '';
document.body.appendChild(clipboardDiv);
function copyHtmlToClipboard(html) {
clipboardDiv.innerHTML=html;
var focused=document.activeElement;
clipboardDiv.focus();
window.getSelection().removeAllRanges();
var range = document.createRange();
range.setStartBefore(clipboardDiv.firstChild);
range.setEndAfter(clipboardDiv.lastChild);
window.getSelection().addRange(range);
var ok=false;
try {
if (document.execCommand('copy')) ok=true; else utils.log('execCommand returned false !');
} catch (err) {
utils.log('execCommand failed ! exception '+err);
}
focused.focus();
}
see jsfiddle where you can enter html segment into the textarea and copy to the clipboard with ctrl+c.
How do I copy to the clipboard in JavaScript?
Overview
There are three primary browser APIs for copying to the clipboard:
Async Clipboard API
[navigator.clipboard.writeText]
- Text-focused portion available in Chrome 66 (March 2018)
- Access is asynchronous and uses JavaScript Promises, can be written so security user prompts (if displayed) don't interrupt the JavaScript in the page.
- Text can be copied to the clipboard directly from a variable.
- Only supported on pages served over HTTPS.
- In Chrome 66 pages inactive tabs can write to the clipboard without a permissions prompt.
document.execCommand('copy')
(deprecated) /p>- Most browsers support this as of ~April 2015 (see Browser Support below).
- Access is synchronous, i.e. stops JavaScript in the page until complete including displaying and user interacting with any security prompts.
- Text is read from the DOM and placed on the clipboard.
- During testing ~April 2015 only Internet Explorer was noted as displaying permissions prompts whilst writing to the clipboard.
Overriding the copy event
- See Clipboard API documentation on Overriding the copy event.
- Allows you to modify what appears on the clipboard from any copy event, can include other formats of data other than plain text.
- Not covered here as it doesn't directly answer the question.
General development notes
Don't expect clipboard related commands to work whilst you are testing code in the console. Generally, the page is required to be active (Async Clipboard API) or requires user interaction (e.g. a user click) to allow (document.execCommand('copy')
) to access the clipboard see below for more detail.
IMPORTANT (noted here 2020/02/20)
Note that since this post was originally written deprecation of permissions in cross-origin IFRAMEs and other IFRAME "sandboxing" prevents the embedded demos "Run code snippet" buttons and "codepen.io example" from working in some browsers (including Chrome and Microsoft Edge).
To develop create your own web page, serve that page over an HTTPS connection to test and develop against.
Here is a test/demo page which demonstrates the code working:
https://deanmarktaylor.github.io/clipboard-test/
Async + Fallback
Due to the level of browser support for the new Async Clipboard API, you will likely want to fall back to the document.execCommand('copy')
method to get good browser coverage.
Here is a simple example (may not work embedded in this site, read "important" note above):
function fallbackCopyTextToClipboard(text) {
var textArea = document.createElement("textarea");
textArea.value = text;
// Avoid scrolling to bottom
textArea.style.top = "0";
textArea.style.left = "0";
textArea.style.position = "fixed";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
console.log('Fallback: Copying text command was ' + msg);
} catch (err) {
console.error('Fallback: Oops, unable to copy', err);
}
document.body.removeChild(textArea);
}
function copyTextToClipboard(text) {
if (!navigator.clipboard) {
fallbackCopyTextToClipboard(text);
return;
}
navigator.clipboard.writeText(text).then(function() {
console.log('Async: Copying to clipboard was successful!');
}, function(err) {
console.error('Async: Could not copy text: ', err);
});
}
var copyBobBtn = document.querySelector('.js-copy-bob-btn'),
copyJaneBtn = document.querySelector('.js-copy-jane-btn');
copyBobBtn.addEventListener('click', function(event) {
copyTextToClipboard('Bob');
});
copyJaneBtn.addEventListener('click', function(event) {
copyTextToClipboard('Jane');
});
<div style="display:inline-block; vertical-align:top;">
<button class="js-copy-bob-btn">Set clipboard to BOB</button><br /><br />
<button class="js-copy-jane-btn">Set clipboard to JANE</button>
</div>
<div style="display:inline-block;">
<textarea class="js-test-textarea" cols="35" rows="4">Try pasting into here to see what you have on your clipboard:
</textarea>
</div>
Related Topics
Our App Crashed in iOS 9 Which Upload by Xcode 10
Getting Video Snapshot for Thumbnail
Chrome iOS - Is It Just a Uiwebview
How to Pop Specific View Controller in Swift
How to Get 1 Hour Ago from a Date in iOS Swift
iOS 12 Terminates Apps in the Background for No Reason
Ipad: Drag a Uitableviewcell from One Uitableviewcontroller and Drop It into Another
How to Add a Footer to a Uitableview in Storyboard
Don't Launch Simulator When Running Unittests
In Swift: Difference Between Array VS Nsarray VS [Anyobject]
How to Add a Toolbar to the Bottom of a Uitableviewcontroller in Storyboards
Align Button Image to Right Edge of Uibutton
iOS Get Heart Rate from Apple Watch in Near Real Time
iOS Static Framework with Resources Inside
How Does One Print All Wkwebview on and Offscreen Content Osx and iOS
Memory Leak with Large Core Data Batch Insert in Swift
iOS App Won't Run on Device Any More Under iOS 12: Unrecoverable Ct Signature Issue