I'm using this function to copy a URL to the clipboard:
我正在使用此功能将URL复制到剪贴板:
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?
在桌面浏览器上一切正常,但在我的函数成功返回的iOS设备上没有,但数据根本没有复制到剪贴板。造成这种情况的原因是什么?我该如何解决这个问题?
5 个解决方案
#1
61
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:
看起来在选择范围和一些小黑客的帮助下,可以直接复制到iOS(> = 10)Safari上的剪贴板。我亲自在iPhone 5C iOS 10.3.3和iPhone 8 iOS 11.1上测试了这个。但是,似乎有一些限制,它们是:
- 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. - 保存元素的contenteditable和readonly的旧值,以便在复制后能够恢复它们。
- Change
contenteditable
totrue
andreadonly
tofalse
. - 将contenteditable更改为true,将readonly更改为false。
- 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. - 恢复以前的contenteditable和readonly值。
- Run
execCommand('copy')
. - 运行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.
现在,这看起来有点复杂,而且发布复制命令太麻烦了,所以我不确定这是Apple的预期设计选择,但是谁知道......同时,这个目前是有效的在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).
有了这个说法,像这样的polyfills可用于简化此操作并使其跨浏览器兼容(感谢@Toskan在评论中的链接)。
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>
.
请注意,此函数的el参数必须是或
Old answer: previous iOS versions
On iOS < 10 there are some restrictions for Safari (which actually are security measures) to the Clipboard API:
在iOS <10上,对Clipboard API的Safari(实际上是安全措施)有一些限制:
- 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). - 它只支持通过快捷键读取/写入OS剪贴板,而不是通过document.execCommand()。注意,“shorcut key”表示一些可点击的(例如复制/粘贴动作菜单或自定义iOS键盘快捷键)或物理键(例如连接的蓝牙键盘)。
- It doesn't support the
ClipboardEvent
constructor. - 它不支持ClipboardEvent构造函数。
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.
因此(至少截至目前),不可能使用Javascript以编程方式在iOS设备上复制剪贴板中的某些文本/值。只有用户才能决定是否复制某些内容。
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.
但是,可以通过编程方式选择某些内容,这样用户只需点击选择中显示的“复制”工具提示即可。这可以使用与上面完全相同的代码来实现,只需删除execCommand('copy'),这确实不起作用。
#2
32
I've searched for some solutions and I've found one that actually works: http://www.seabreezecomputers.com/tips/copy2clipboard.htm
我搜索了一些解决方案,我找到了一个实际可行的解决方案:http://www.seabreezecomputers.com/tips/copy2clipboard.htm
Basically, example could be something like:
基本上,示例可能是这样的:
var $input = $(' some input/textarea ');
$input.val(result);
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
var el = $input.get(0);
var editable = el.contentEditable;
var readOnly = el.readOnly;
el.contentEditable = true;
el.readOnly = false;
var range = document.createRange();
range.selectNodeContents(el);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
el.setSelectionRange(0, 999999);
el.contentEditable = editable;
el.readOnly = readOnly;
} else {
$input.select();
}
document.execCommand('copy');
$input.blur();
#3
9
For security reasons iOS Safari only allows document.execCommand('copy')
for text within a contentEditable
container.
出于安全原因,iOS Safari仅允许document.execCommand('copy')用于contentEditable容器中的文本。
The workaround is to detect iOS Safari and quickly toggle contentEditable
before executing document.execCommand('copy')
.
解决方法是检测iOS Safari并在执行document.execCommand('copy')之前快速切换contentEditable。
The following function should work in all browsers/devices and accepts a CSS Selector or HTMLElement:
以下函数应该适用于所有浏览器/设备,并接受CSS Selector或HTMLElement:
function copyToClipboard(el) {
// resolve the element
el = (typeof el === 'string') ? document.querySelector(el) : el;
// handle iOS as a special case
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
// save current contentEditable/readOnly status
var editable = el.contentEditable;
var readOnly = el.readOnly;
// convert to editable with readonly to stop iOS keyboard opening
el.contentEditable = true;
el.readOnly = true;
// create a selectable range
var range = document.createRange();
range.selectNodeContents(el);
// select the range
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
el.setSelectionRange(0, 999999);
// restore contentEditable/readOnly to original state
el.contentEditable = editable;
el.readOnly = readOnly;
}
else {
el.select();
}
// execute copy command
document.execCommand('copy');
}
input { font-size: 14px; font-family: tahoma; }
button { font-size: 14px; font-family: tahoma; }
<input class="important-message" type="text" value="Hello World" />
<button onclick="copyToClipboard('.important-message')">Copy</button>
#4
4
Please check my solution.
请检查我的解决方案。
It works on Safari (tested on iPhone 7 and iPad) and on other browsers.
它适用于Safari(在iPhone 7和iPad上测试)和其他浏览器。
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/
https://gist.github.com/rproenca/64781c6a1329b48a455b645d361a9aa3 https://fiddle.jshell.net/k9ejqmqt/1/
Hope that helps you.
希望对你有所帮助。
Regards.
问候。
#5
-1
<input id="copyIos" type="hidden" value="">
var clipboard = new Clipboard('.copyUrl');
//兼容ios复制
$('.copyUrl').on('click',function() {
var $input = $('#copyIos');
$input.val(share_url);
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
clipboard.on('success', function(e) {
e.clearSelection();
$.sDialog({
skin: "red",
content: 'copy success!',
okBtn: false,
cancelBtn: false,
lock: true
});
console.log('copy success!');
});
} else {
$input.select();
}
//document.execCommand('copy');
$input.blur();
});
#1
61
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:
看起来在选择范围和一些小黑客的帮助下,可以直接复制到iOS(> = 10)Safari上的剪贴板。我亲自在iPhone 5C iOS 10.3.3和iPhone 8 iOS 11.1上测试了这个。但是,似乎有一些限制,它们是:
- 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. - 保存元素的contenteditable和readonly的旧值,以便在复制后能够恢复它们。
- Change
contenteditable
totrue
andreadonly
tofalse
. - 将contenteditable更改为true,将readonly更改为false。
- 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. - 恢复以前的contenteditable和readonly值。
- Run
execCommand('copy')
. - 运行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.
现在,这看起来有点复杂,而且发布复制命令太麻烦了,所以我不确定这是Apple的预期设计选择,但是谁知道......同时,这个目前是有效的在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).
有了这个说法,像这样的polyfills可用于简化此操作并使其跨浏览器兼容(感谢@Toskan在评论中的链接)。
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>
.
请注意,此函数的el参数必须是或
Old answer: previous iOS versions
On iOS < 10 there are some restrictions for Safari (which actually are security measures) to the Clipboard API:
在iOS <10上,对Clipboard API的Safari(实际上是安全措施)有一些限制:
- 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). - 它只支持通过快捷键读取/写入OS剪贴板,而不是通过document.execCommand()。注意,“shorcut key”表示一些可点击的(例如复制/粘贴动作菜单或自定义iOS键盘快捷键)或物理键(例如连接的蓝牙键盘)。
- It doesn't support the
ClipboardEvent
constructor. - 它不支持ClipboardEvent构造函数。
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.
因此(至少截至目前),不可能使用Javascript以编程方式在iOS设备上复制剪贴板中的某些文本/值。只有用户才能决定是否复制某些内容。
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.
但是,可以通过编程方式选择某些内容,这样用户只需点击选择中显示的“复制”工具提示即可。这可以使用与上面完全相同的代码来实现,只需删除execCommand('copy'),这确实不起作用。
#2
32
I've searched for some solutions and I've found one that actually works: http://www.seabreezecomputers.com/tips/copy2clipboard.htm
我搜索了一些解决方案,我找到了一个实际可行的解决方案:http://www.seabreezecomputers.com/tips/copy2clipboard.htm
Basically, example could be something like:
基本上,示例可能是这样的:
var $input = $(' some input/textarea ');
$input.val(result);
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
var el = $input.get(0);
var editable = el.contentEditable;
var readOnly = el.readOnly;
el.contentEditable = true;
el.readOnly = false;
var range = document.createRange();
range.selectNodeContents(el);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
el.setSelectionRange(0, 999999);
el.contentEditable = editable;
el.readOnly = readOnly;
} else {
$input.select();
}
document.execCommand('copy');
$input.blur();
#3
9
For security reasons iOS Safari only allows document.execCommand('copy')
for text within a contentEditable
container.
出于安全原因,iOS Safari仅允许document.execCommand('copy')用于contentEditable容器中的文本。
The workaround is to detect iOS Safari and quickly toggle contentEditable
before executing document.execCommand('copy')
.
解决方法是检测iOS Safari并在执行document.execCommand('copy')之前快速切换contentEditable。
The following function should work in all browsers/devices and accepts a CSS Selector or HTMLElement:
以下函数应该适用于所有浏览器/设备,并接受CSS Selector或HTMLElement:
function copyToClipboard(el) {
// resolve the element
el = (typeof el === 'string') ? document.querySelector(el) : el;
// handle iOS as a special case
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
// save current contentEditable/readOnly status
var editable = el.contentEditable;
var readOnly = el.readOnly;
// convert to editable with readonly to stop iOS keyboard opening
el.contentEditable = true;
el.readOnly = true;
// create a selectable range
var range = document.createRange();
range.selectNodeContents(el);
// select the range
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
el.setSelectionRange(0, 999999);
// restore contentEditable/readOnly to original state
el.contentEditable = editable;
el.readOnly = readOnly;
}
else {
el.select();
}
// execute copy command
document.execCommand('copy');
}
input { font-size: 14px; font-family: tahoma; }
button { font-size: 14px; font-family: tahoma; }
<input class="important-message" type="text" value="Hello World" />
<button onclick="copyToClipboard('.important-message')">Copy</button>
#4
4
Please check my solution.
请检查我的解决方案。
It works on Safari (tested on iPhone 7 and iPad) and on other browsers.
它适用于Safari(在iPhone 7和iPad上测试)和其他浏览器。
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/
https://gist.github.com/rproenca/64781c6a1329b48a455b645d361a9aa3 https://fiddle.jshell.net/k9ejqmqt/1/
Hope that helps you.
希望对你有所帮助。
Regards.
问候。
#5
-1
<input id="copyIos" type="hidden" value="">
var clipboard = new Clipboard('.copyUrl');
//兼容ios复制
$('.copyUrl').on('click',function() {
var $input = $('#copyIos');
$input.val(share_url);
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
clipboard.on('success', function(e) {
e.clearSelection();
$.sDialog({
skin: "red",
content: 'copy success!',
okBtn: false,
cancelBtn: false,
lock: true
});
console.log('copy success!');
});
} else {
$input.select();
}
//document.execCommand('copy');
$input.blur();
});