在 iOS 中使用 Javascript 复制到剪贴板

IT技术 javascript ios copy mobile-safari clipboard
2021-01-20 12:26:42

我正在使用此函数将 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();
}

在桌面浏览器上一切正常,但在 iOS 设备上却没有,我的函数成功返回,但数据根本没有复制到剪贴板。这是什么原因造成的,我该如何解决这个问题?

6个回答

更新!iOS >= 10

看起来在选择范围和一些小技巧的帮助下,可以直接复制到 iOS (>= 10) Safari 上的剪贴板。我个人在 iPhone 5C iOS 10.3.3 和 iPhone 8 iOS 11.1 上对此进行了测试。但是,似乎有一些限制,它们是:

  1. 文本只能从<input><textarea>元素复制
  2. 如果保存文本的元素不在a 内<form>,则它必须在contenteditable.
  3. 包含文本的元素不能readonly(尽管您可以尝试,但这不是任何地方都记录的“官方”方法)。
  4. 元素内的文本必须在选择范围内。

要满足所有这四个“要求”,您必须:

  1. 将要复制的文本放入<input>or<textarea>元素中。
  2. 保存元素contenteditable的旧值,readonly以便在复制后能够恢复它们。
  3. 更改contenteditabletruereadonlyfalse
  4. 创建一个范围以选择所需的元素并将其添加到窗口的选择中。
  5. 设置整个元素选择范围
  6. 恢复以前的contenteditablereadonly值。
  7. 运行execCommand('copy')

这将导致用户设备的插入符号移动并选择您想要的元素中的所有文本,然后自动发出复制命令。用户将看到正在选择的文本,并会显示带有选择/复制/粘贴选项的工具提示。

现在,这看起来有点复杂,只是发出复制命令太麻烦了,所以我不确定这是 Apple 的预期设计选择,但谁知道......同时,这目前有效在 iOS >= 10 上

话虽如此,像这样的polyfill可用于简化此操作并使其跨浏览器兼容(感谢@Toskan在评论中提供链接)。

工作示例

总而言之,您需要的代码如下所示:

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');
}

请注意,el此函数参数必须是 an<input>或 a <textarea>

旧答案:以前的 iOS 版本

iOS < 10上,Safari(实际上是安全措施)对Clipboard API有一些限制

  • copy仅在有效选择上触发事件,cut并且paste仅在聚焦的可编辑字段中触发事件
  • 它仅支持通过快捷键读取/写入操作系统剪贴板,不支持通过document.execCommand(). 请注意,“快捷键”是指一些可点击的(例如复制/粘贴操作菜单或自定义 iOS 键盘快捷键)或物理键(例如连接的蓝牙键盘)。
  • 它不支持ClipboardEvent构造函数。

因此(至少到目前为止)不可能使用 Javascript 以编程方式复制 iOS 设备剪贴板中的某些文本/值只有用户可以决定是否复制某些内容。

然而,可以通过编程方式选择某些内容,这样用户只需点击选择中显示的“复制”工具提示。这可以使用与上面完全相同的代码来实现,只需删除execCommand('copy'),这确实不起作用。

以编程方式几乎总是意味着来自用户触发的事件,例如点击......但它仍然对我不起作用
2021-03-19 12:26:42
@Cymro 是的,它是 iOS 独有的。您不需要所有这些东西在 Windows 上执行此操作。有很多帖子和答案解释了如何做到这一点。
2021-03-21 12:26:42
@MarcoBonelli,很好的答案。我有一个相关的问题,当用户按下复制(iOS 键盘快捷键)时,我需要将他/她重定向到另一个页面。怎么做?
2021-03-23 12:26:42
@DominicTobias 如果您花两分钟阅读我的答案,也许您会明白为什么它不起作用。我的字面意思是“不可能以编程方式复制 [...]”
2021-03-28 12:26:42
@ Peege151“快捷键”是指一些可点击的(例如普通的复制/粘贴操作菜单或自定义 iOS 键盘快捷键)或物理键(例如连接的蓝牙键盘等)。无论如何,这是由用户触发的,而不是以编程方式触发的。
2021-03-29 12:26:42

我搜索了一些解决方案,我找到了一个真正有效的解决方案:http : //www.seabreezecomputers.com/tips/copy2clipboard.htm

基本上,例子可能是这样的:

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();
它适用于 IOS 10,谢谢!只是您的示例中的一个小细节,替换“result”,这是您要放入剪贴板的实际文本的未定义变量。
2021-03-14 12:26:42
适用于我的 iOS 10 设备!
2021-03-17 12:26:42
作品。但它会在 iOS 上打开键盘并在一瞬间将其关闭。但是你可以看到键盘。
2021-03-26 12:26:42
非常感谢,您可以通过将 readOnly 设置为 true 而不是 false @pixelscreen 来完全避免使用键盘
2021-04-01 12:26:42
是的!我确认@DominicTobias 的评论(设置 readOnly = true)也有效。
2021-04-08 12:26:42

这是我的跨浏览器实现

您可以通过运行下面的代码段来测试它

例子:

copyToClipboard("Hello World");

/**
 * Copy a string to clipboard
 * @param  {String} string         The string to be copied to clipboard
 * @return {Boolean}               returns a boolean correspondent to the success of the copy operation.
 * @see https://stackoverflow.com/a/53951634/938822
 */
function copyToClipboard(string) {
  let textarea;
  let result;

  try {
    textarea = document.createElement('textarea');
    textarea.setAttribute('readonly', true);
    textarea.setAttribute('contenteditable', true);
    textarea.style.position = 'fixed'; // prevent scroll from jumping to the bottom when focus is set.
    textarea.value = string;

    document.body.appendChild(textarea);

    textarea.focus();
    textarea.select();

    const range = document.createRange();
    range.selectNodeContents(textarea);

    const sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);

    textarea.setSelectionRange(0, textarea.value.length);
    result = document.execCommand('copy');
  } catch (err) {
    console.error(err);
    result = null;
  } finally {
    document.body.removeChild(textarea);
  }

  // manual copy fallback using prompt
  if (!result) {
    const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
    const copyHotkey = isMac ? '⌘C' : 'CTRL+C';
    result = prompt(`Press ${copyHotkey}`, string); // eslint-disable-line no-alert
    if (!result) {
      return false;
    }
  }
  return true;
}
Demo: <button onclick="copyToClipboard('It works!\nYou can upvote my answer now :)') ? this.innerText='Copied!': this.innerText='Sorry :(' ">Click here</button>

<p>
  <textarea placeholder="(Testing area) Paste here..." cols="80" rows="4"></textarea>
</p>

注意:当它不是由用户启动时它不起作用,比如超时或任何异步事件!

它必须来自受信任的事件,例如从click按钮上的事件调用

当它不是来自像点击事件这样的受信任事件时,知道如何做到这一点吗?
2021-03-26 12:26:42
在 safari ios 和 chrome web 上测试对我有用。
2021-03-27 12:26:42
为我在 Edge 上工作,尚未在 iOS 上测试
2021-04-11 12:26:42
建议:textarea.focus();从建议的解决方案中删除- 否则它会向下滚动,无论设置如何textarea.style.position = 'fixed';
2021-04-12 12:26:42

问题: iOS Safari 只允许document.execCommand('copy')contentEditable容器内显示文本

解决方案:检测iOS Safari并contentEditable在执行之前快速切换document.execCommand('copy')

以下功能适用于所有浏览器。使用CSS SelectorHTMLElement调用

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>

注意:在使用 iOS 10 和 11 时,我通过上述方法发现了一些额外的注意事项。 a) 输入需要有足够的宽度。如果您希望复制用户看不到的输入,则在 CSS 中将宽度设置为 0 或 1px 将不起作用。(多大?谁知道?)在屏幕外设置一个相对位置似乎还是可以的。b) 如果你添加 event.preventDefault() 到这里,注意它会导致键盘输入(或表单导航输入?)弹出来切换,否定使用的效果readOnly希望对其他人有帮助!
2021-04-08 12:26:42

请检查我的解决方案。

它适用于 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/

希望能帮到你。

问候。