简单的撸一个一键复制
相信做前端的小伙伴们,无论是PC端还是H5, 都或多或少会接触到'一键复制'这样的需求。今天就针对H5,来撸一个简单的Clipboard。
知识准备
- document.execCommand: 执行一个相对当前文档的命令。
document.execCommand(commandName, showDefaultUi, defaultValue) /* * commandName: 命令名称,如: copy, backColor, ClearAuthenticationCache等。 * showDefaultUi: 是否展示用户界面,默认值为false。 * defaultValue: 一些命令需要的默认参数,比如insetImage 的 src。 */
兼容性:
mdn doc: https://developer.mozilla.org/zh-CN/docs/Web/API/Document/execCommand
- command: copy: 拷贝当前选中内容到剪贴板(划重点)
mdn doc: https://developer.mozilla.org/zh-CN/docs/Web/Events/copy
- window.getSelection: 返回一个selection对象,表示用户选择的文本范围或光标的当前位置。一般我们使用toString()方法来获取当前选中的字符串。
- 兼容性:
mdn doc: https://developer.mozilla.org/zh-CN/docs/Web/API/Window/getSelection
- setSelectionRange(for inputElement): 可以从一个focus的input元素中选定指定起始位置的内容。
inpurElement.setSelectionRange(selectionStart, selectionEnd, selectionDirection) /* * selectionStart: 被选中的第一个字符的位置 * selectionEnd: 被选中的最后一个字符的下一个位置 * selectionDirection: 选中方向 */
兼容性:
mdn doc: https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLInputElement/setSelectionRange
思考
我们主要使用的是浏览器给我们提供的copy命令。而copy命令通过复制被选中区域到系统剪贴板。这里我们可以联想到我们的setSelectionRange方法可以将一个聚焦状态下的input元素中的内容按照自己指定的起始位置进行选中效果。嘛~这里我们整个clipboard的思路就已经讲完了。下面我们看看代码具体怎么实现。
code
step 1
监听copy命令: 原因是不止是我们使用document.execCommand('copy')命令来触发复制命令,使用键盘的ctrl + c也是可以触发的。所以要面面俱到。
/** * 1. 全局监听copy方法 * 2. 将获取到copy的字符串存储到本地存储中,确保数据不会丢失 */ document.addEventListener('copy', () => { const value = window.getSelection()?.toString() FACKSETSTRONGE({ key: CLIPBOARDNAME, value }).catch(error => {}) })
step2
这个过程中我们需要动态的创建input的元素来为我们达到选中这个条件,所以,我们单独的将生成input的方法封装出来。
/** * createInput for fack selection * @params value<String> */ fackInput(value = '') { const FACKINPUT = document.createElement('input') FACKINPUT.setAttribute('readonly', 'readonly') FACKINPUT.setAttribute('value', value) FACKINPUT.style.cssText = ` position: fixed; top: -200000px; left: -200000px; ` document.body.appendChild(FACKINPUT) FACKINPUT.focus() this.FACKINPUT = FACKINPUT }
step3
动态创建也要动态销毁,避免内存泄漏。
/** * removeFackInput * @params fackInput<HTMLELEMENT> */ removeFackInput(fackInput = null) { if (fackInput) { document.body.removeChild(fackInput) } }
step4
设置剪贴板内容,大概逻辑过程为: 首先判断是否有设置内容,若无抛出错误。若存在设置内容,首先存储到本地存储,之后通过document.execCommand('copy')来判断是否可以使用copy命令。并按照上面我们的思路将内容设置到剪贴板。
/** * 设置系统clipboard * @params options<Object> { data<String> } */ _set({ data = '' }) { return new Promise((resolve, reject) => { /* 若无设置内容抛出异常 */ if (!data) { reject(GENERATIONMSG({ code: COMMONERROR, data: 'the action of setclipboard need a value' })) } /* 首先设置本地存储防止数据丢失 */ FACKSETSTRONGE({ key: CLIPBOARDNAME, value: data }).then(() => { if (document.execCommand('copy')) { this.fackInput(data) if (this.FACKINPUT.setSelectionRange) { this.FACKINPUT.setSelectionRange(0, this.FACKINPUT.value.length) document.execCommand('copy') this.removeFackInput() resolve(GENERATIONMSG({ code: SUCCESSCODE, data })) } } else { reject(GENERATIONMSG({ code: COMMONERROR, data: 'the systerm do not have a command copy' })) } }).catch(error => { reject(GENERATIONMSG({ code: COMMONERROR, data: error })) }) }) }
step5
获取剪贴板内容, 当然你可以直接ctrl + v, 或者在手机上长按呼出黏贴操作。若主动调用则需使用此方法。其实就是简单的从本地存储中取出来。
/** * 获取系统clipboard */ _get() { return new Promise((resolve, reject) => { FACKGETSTRONGE({ key: CLIPBOARDNAME }).then(({ data = '' }) => { resolve(GENERATIONMSG({ code: SUCCESSCODE, data })) }).catch(error => { reject(GENERATIONMSG({ code: COMMONERROR, data: error })) }) }) }
step6
使用方法。
<script src='path/clipboard.min.js'></script> <script> // _clipboard 已经默认挂载到window上了,可以直接使用 // set _clipboard._set({ data: 'your clipboard data' }).then(res => successHandler).catch(error => errorHandler) // get _clipboard._get().then(res => console.log(res.data)).catch(error => errorHandler) </script>
至此,一个简单的clipboard api编写完毕了。可以满足我们日常的一键复制需求。希望大家从中可以学到新的知识点。yeah~
完整代码如下
当中部分方法可以移步github地址来查看: https://github.com/mobile-plugin/ClipBoardForH5
/** * clipboard */ class Clipboard { constructor() { /** * 1. 全局监听copy方法 * 2. 将获取到copy的字符串存储到本地存储中,确保数据不会丢失 */ document.addEventListener('copy', () => { const value = window.getSelection()?.toString() FACKSETSTRONGE({ key: CLIPBOARDNAME, value }).catch(error => {}) }) } /** * 设置系统clipboard * @params options<Object> { data<String> } */ _set({ data = '' }) { return new Promise((resolve, reject) => { /* 若无设置内容抛出异常 */ if (!data) { reject(GENERATIONMSG({ code: COMMONERROR, data: 'the action of setclipboard need a value' })) } /* 首先设置本地存储防止数据丢失 */ FACKSETSTRONGE({ key: CLIPBOARDNAME, value: data }).then(() => { if (document.execCommand('copy')) { this.fackInput(data) if (this.FACKINPUT.setSelectionRange) { this.FACKINPUT.setSelectionRange(0, this.FACKINPUT.value.length) document.execCommand('copy') this.removeFackInput() resolve(GENERATIONMSG({ code: SUCCESSCODE, data })) } } else { reject(GENERATIONMSG({ code: COMMONERROR, data: 'the systerm do not have a command copy' })) } }).catch(error => { reject(GENERATIONMSG({ code: COMMONERROR, data: error })) }) }) } /** * 获取系统clipboard */ _get() { return new Promise((resolve, reject) => { FACKGETSTRONGE({ key: CLIPBOARDNAME }).then(({ data = '' }) => { resolve(GENERATIONMSG({ code: SUCCESSCODE, data })) }).catch(error => { reject(GENERATIONMSG({ code: COMMONERROR, data: error })) }) }) } /** * createInput for fack selection * @params value<String> */ fackInput(value = '') { const FACKINPUT = document.createElement('input') FACKINPUT.setAttribute('readonly', 'readonly') FACKINPUT.setAttribute('value', value) FACKINPUT.style.cssText = ` position: fixed; top: -200000px; left: -200000px; ` document.body.appendChild(FACKINPUT) FACKINPUT.focus() this.FACKINPUT = FACKINPUT } /** * removeFackInput * @params fackInput<HTMLELEMENT> */ removeFackInput(fackInput = null) { if (fackInput) { document.body.removeChild(fackInput) } } }
你也可以关注我的公众号