1. 背景
以下是对 iOS 下复制功能的简单实现,注意在 iOS 10 以下的版本中,以下代码是无效的。
最近要实现一个小需求:iOS APP下,用户点击一个按钮,系统自动复制一段文本到系统剪贴板。通过查资料发现,iOS 出于安全性的考虑,对 Clipboard API 的使用有诸多限制。但是在 iOS 10 及以上版本中,可以通过 hack 的方式来实现该功能。
2. 方案
主要参考的是 SO 上的这个答案,在 iOS 10 及以上版本中,使用复制功能有以下限制:
- 只能复制
<input>
或<textarea>
元素中的文本; - 如果包含待复制文本的元素没有包含在一个
<form>
中,那它的contentEditable
属性必须为true
; - 第2步中的元素同时不能是
readonly
; - 待复制文本必须是
被选中
状态。
要满足上述4个限制,代码中需要做到:
- 把待复制文本放入
<input>
或<textarea>
类型的元素 A 中; - 保存 A 元素的
contentEditable
和readonly
属性,以便复制完成后恢复现场; - 设置 A 元素的
contentEditable
为true
,readonly
属性为false
; - 创建一个
range
对象并挂载 A 元素; - 获取窗口当前选中元素并清除,然后设置选中元素为第4步创建的
range
对象; - 视情况恢复元素 A 的
contentEditable
和readonly
属性; - 执行
document.execCommand('copy')
。
最终实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
const el = document.createElement('input'); const btn = document.getElementById('btn'); el.value = '待复制文本'; el.style.opacity = '0'; document.body.appendChild(el); const editable = el.contentEditable; const readOnly = el.readOnly; el.contentEditable = true; el.readOnly = false; const range = document.createRange(); range.selectNodeContents(el); const sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); el.setSelectionRange(0, 999999); el.contentEditable = editable; el.readOnly = readOnly; const ret = document.execCommand('copy'); el.blur(); btn.text = ret ? '已复制' : '复制失败'; |
上述代码中需要注意:
- 不能将
el
的display
设置为none
,或者将其visibility
设置为hidden
,所以这里将它的透明度设置为0; - 设置元素被选中后,Safari 下键盘会弹出,所以在复制后执行
el.blur()
来使其失去焦点,但页面在某些浏览器下还是会有个轻微抖动; - 通过执行
el.select()
也能使其获得焦点,但该方法在 Mac 下 Chrome 中有效,在 iOS 下无效。
注:转载注明出处并联系作者,本文链接:https://nodefe.com/ios-js-copy/
最后一句应该放到文首
哈哈,你说的很有道理,已修改