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

Nin*_*ili 67 javascript clipboard copy mobile-safari ios

我正在使用此功能将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();
}
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*lli 116

更新!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><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');
}
Run Code Online (Sandbox Code Playgroud)

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

旧答案:以前的iOS版本

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

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

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

但是,可以通过编程方式选择某些内容,这样用户只需点击选择中显示的"复制"工具提示即可.这可以使用与上面完全相同的代码来实现,只需删除execCommand('copy'),这实际上是行不通的.


Mar*_*vic 46

我已经搜索了一些解决方案,我找到了一个实际可行的解决方案: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();
Run Code Online (Sandbox Code Playgroud)

  • 适用于我的iOS 10设备! (4认同)
  • 是啊工作!我确认@DominicTobias的评论(设置readOnly = true)也有效. (3认同)
  • 作品。但它在 iOS 上打开键盘并瞬间关闭。但你可以看到键盘。 (2认同)
  • 非常感谢,您可以通过将readOnly设置为true而不是false @pixelscreen来完全避免使用键盘 (2认同)

Vit*_*.us 23

这是我的跨浏览器实现

您可以通过运行以下代码段进行测试

例:

copyToClipboard("Hello World");
Run Code Online (Sandbox Code Playgroud)

copyToClipboard("Hello World");
Run Code Online (Sandbox Code Playgroud)
/**
 * 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.
 */
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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 建议:从建议的解决方案中删除`textarea.focus();`-否则它会向下滚动,而与设置`textarea.style.position ='fixed'无关;` (2认同)

Joh*_*rty 22

出于安全原因,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');
}
Run Code Online (Sandbox Code Playgroud)
input { font-size: 14px; font-family: tahoma; }
button { font-size: 14px; font-family: tahoma; }
Run Code Online (Sandbox Code Playgroud)
<input class="important-message" type="text" value="Hello World" />
<button onclick="copyToClipboard('.important-message')">Copy</button>
Run Code Online (Sandbox Code Playgroud)

  • 注意:在使用 iOS 10 和 11 时,我发现上述方法还有一些额外的注意事项。a) 输入需要有足够的宽度。如果您希望复制用户看不到的输入,则在 CSS 中设置 0 或 1px 的宽度将不起作用。(有多大?谁知道?)在屏幕外设置相对位置似乎仍然没问题。b) 如果向其中添加 event.preventDefault(),请注意,它将导致键盘输入(或表单导航输入?)弹出窗口切换,从而抵消使用“readOnly”的效果。希望对其他人有帮助! (2认同)

Rod*_*igo 11

请检查我的解决方案.

它适用于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');
Run Code Online (Sandbox Code Playgroud)

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

希望对你有所帮助.

问候.


小智 6

iOS 13.4 及更高版本

从版本 13.4 开始,iOS Safari 支持现代异步剪贴板 API:

与 JavaScript 中的所有内容一样,新的 API 大约好 1000 倍,但您仍然需要大量的后备代码,因为您的许多用户将使用旧版本多年。

以下是如何将新的剪贴板 API 与原始问题中的代码一起使用:

function CopyUrl($this){
  var querySelector = $this.next().attr("id");
  var emailLink = document.querySelector("#"+querySelector);

  if (navigator.clipboard) {
    var myText = emailLink.textContent;
    navigator.clipboard.writeText(myText).then(function() {
      // Do something to indicate the copy succeeded
    }).catch(function() {
      // Do something to indicate the copy failed
    });
  } else {
    // Here's where you put the fallback code for older browsers.
  }
}
Run Code Online (Sandbox Code Playgroud)


Eri*_*and 5

我的解决方案是通过结合此页面上的其他答案创建的。

与其他答案不同,它不需要您在页面上已经有一个元素。它将创建自己的文本区域,然后清理混乱。

function copyToClipboard(str) {
    var el = document.createElement('textarea');
    el.value = str;
    el.setAttribute('readonly', '');
    el.style = {position: 'absolute', left: '-9999px'};
    document.body.appendChild(el);

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

    document.execCommand('copy');
    document.body.removeChild(el);
}
Run Code Online (Sandbox Code Playgroud)