前端技术之---复制文本
仅供参考
文章目录
- 一、引言
- 二、方法一:创建隐藏DOM元素
- 三、方法二:使用现代浏览器的 Clipboard API
- 四、封装成工具
- 五、自定义指令
一、引言
看到这个图标,大家应该都很熟悉,往往放在某行文本旁边,点击即可复制文本内容,这篇文章将仔细说明如何实现“复制
”的功能
上面说到是点击之后实现了复制,那么自然而然地可以想到使用 el-button 按钮,里面装着这个图标,点击图标实际上是点击按钮,触发 click 事件,也就是执行了某个函数,实现了复制,可以看出复制的本质是某个函数的执行
二、方法一:创建隐藏DOM元素
<template><div class="mt-96 flex justify-center gap-4 items-center"><p>{{ content }}</p><ElButton type="primary" @click="copy(content)">复制按钮</ElButton></div>
</template><script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";const content = ref("要复制的内容");const copy = (text) => {return new Promise((resolve, reject) => {try {const input = document.createElement("textarea");input.setAttribute("readonly", "readonly");input.value = text;document.body.appendChild(input);input.select();if (document.execCommand("copy")) document.execCommand("copy");document.body.removeChild(input);ElMessage.success("复制成功");resolve(text);} catch (error) {ElMessage.error("复制失败");reject(error);}});
};
</script>
实现的效果:
点击复制按钮后,弹出成功提示框,粘贴出的内容就是复制的文本
代码解析:
1、为什么要返回一个Promise对象:目的是将其封装为异步操作,复制成功时通过 resolve(text) 返回结果,发生错误时通过 reject(error) 抛出异常;如果复制后还有后续操作,可以通过 .then(text => {})、.catch(error => {})做进一步处理
2、document.createElement(“textarea”) 创建一个文本框
(可以理解为输入框,type 为 textarea),但是这个文本框是隐藏
起来的,无法看到
3、input 是 DOM 对象,也具有方法
;setAttribute(“readonly”, “readonly”) 方法使其只读
,避免用户编辑
4、input.value = text 将需要复制的内容赋值给文本框
5、input.select() 选中文本框中的所有内容
6、document.execCommand(“copy”) 调用浏览器的复制命令
,将选中的内容
复制到剪贴板;这一步才真正实现了复制
;为什么需要做一个判断呢?document.execCommand 函数的返回值为布尔类型,true 表示复制成功,false 表示复制失败,但是早期部分浏览器即使复制成功也会返回 false,所以判断的目的是为了兼容
7、document.body.removeChild(input) 移除临时创建的文本框
这个方法很好理解,但是并不推荐
三、方法二:使用现代浏览器的 Clipboard API
<template><div style="display: flex; gap: 10px; align-items: center;"><p>{{ content }}</p ><ElButton @click="copy(content)">点击复制</ElButton></div>
</template><!-- <script setup>
import { ElButton, ElMessage } from 'element-plus';
import {ref} from 'vue'const content = ref('要复制的内容')const copy = async(text) => {try {// 写入文本到剪贴板await navigator.clipboard.writeText(text);ElMessage.success('文本复制成功')} catch (error) {ElMessage.error('复制失败')}
}
</script> -->
什么是
Clipboard API
?
Clipboard API 是一组用于访问和操作系统剪贴板
的 Web API,允许网页读取和写入剪贴板内容(如文本、图像等),提供了比传统 document.execCommand() 方法更现代、更安全的剪贴板操作方式
1、写入内容到剪贴板:通过writeText()
写入文本,write()
写入更复杂的数据(如带格式文本、图像等)。
2、从剪贴板读取内容:通过readText()
读取文本,read()
读取多种格式的内容
3、所有方法均返回 Promise,需配合 async/await 或 .then() 使用
四、封装成工具
从上面的代码可以看出,每个函数都只是对接收的参数做操作,并没有用到组件中其他的内容,因此可以统一写到一个 JS 文件中,封装成工具,暴露出去,想要使用时通过 import 引入即可,避免在多个组件中都写入相同的代码
1、在src 文件夹下创建 utils 文件夹,专门用于存放工具
2、在 utils 文件夹中创建 copy.js 文件,写入函数
export function copyToClip(text) {return new Promise((resolve, reject) => {try {const input = document.createElement("textarea");input.setAttribute("readonly", "readonly");input.value = text;document.body.appendChild(input);input.select();if (document.execCommand("copy")) document.execCommand("copy");document.body.removeChild(input);resolve(text);} catch (error) {reject(error);}});
}
必须通过 export 将函数暴露出去,否则其他组件无法拿到
不要在工具函数中使用 ElMessage,在组件中调用工具函数后再通过 async/await 后续操作调用 ElMessage
3、在组件中引入并使用工具
<template><div style="margin-top: 400px; display: flex; gap: 10px; align-items: center; justify-content: center;"><p>{{ content }}</p><ElButton type="primary" @click="copyToClip(content)">点击复制</ElButton></div>
</template><script setup>
import {ref} from 'vue'
import { copyToClip } from './utils/copy';const content = ref('要复制的内容')
</script>
可以明显看出,不用在每个组件中重复的写函数,通过引入函数名进行调用即可,大大减少代码量
五、自定义指令
封装成工具后,每次使用都要 import 还是有点麻烦,改造成自定义指令
会更加简洁
首先,自定义指令是什么?
1、Vue 本身提供了 v-model、v-show 等内置指令,是固定的写法;而自定义指令可以随便更改,比如我要使用一个复制的自定义指令,可以是 v-copy,也可以是 v-copyText,由自己设定
2、自定义指令允许将重复的 DOM 操作(如样式修改、事件绑定、动画效果等)封装为可复用的模块,避免在组件中重复编写代码。可以简单的理解为,自定义指令的功能就是函数的功能
书写复制的自定义指令:
import { copyToClip } from '@/utils/copy'const handler = (el: any, binding: any) => {el._copyText = binding.valueif (!el._copyHandler) {el._copyHandler = function (event: Event) {event.stopPropagation() // 阻止事件冒泡到父级copyToClip(this._copyText).then(() => {ElMessage.success(window.$t('common.copied'))})}}el.removeEventListener('click', el._copyHandler)el.addEventListener('click', el._copyHandler)
}
export default {mounted: handler,updated: handler,unmounted: (el: any) => {el.removeEventListener('click', el._copyHandler)}
}
代码解析:
1、函数 handler 中的两个形参,el:给哪个 DOM 元素添加了该自定义指令,那么 el 就是该 DOM 对象;binding:DOM 元素的绑定值,在复制的自定义指令中,binding 得到的就是将要复制的内容
2、el._copyText = binding.value 将被复制的内容赋值给 _copyText
3、el.addEventListener(‘click’, el._copyHandler) 为 DOM 元素添加点击事件,点击事件触发,自动调用 el._copyHandler 函数
4、为什么要对 el._copyHandler 做一个判断:避免重复创建函数;如果没有这个判断,每次 handler 执行都会创建一个 el._copyHandler
5、为什么先移除事件监听,又添加事件监听?因为 handler 可能执行多次,避免添加重复的点击事件
6、export default 导出的是一个对象,mounted、updated 分别是元素挂载到 DOM 后、组件更新后触发 handler 函数
完成这步之后,还不能直接使用自定义指令,需要在 main.js 中全局注册自定义指令
import copy from 从上面的导出app.directive('copy', copy)
前一个 copy 是指令的名字,后一个 copy 是上面 export default 导出的对象
此时,就可以使用自定义指令来完成复制操作了:
<ElButton v-copy="要复制的内容"></ElButton>
该自定义指令的本质是元素挂载后、更新后给元素添加点击事件