JavaScript系列:JS实现复制粘贴文字以及图片

javascript,js · 浏览次数 : 15

小编点评

本文介绍了基于Clipboard API的复制文字、图片等功能的基本概念和使用限制,并给出了实际应用示例。 **主要内容概述如下**: 1. **基于 Clipboard API 复制文字(推荐)基本概念及主要方法使用限制**: 介绍了Clipboard API的概念,包括其提供的方法,如`writeText`、`readText`、`write`和`read`,以及它们的使用限制和安全上下文。还提到了不同浏览器对Clipboard API的支持情况。 2. **基于 document.execCommand('copy')缺陷实际应用示例说明**: 讨论了`document.execCommand('copy')`方法的局限性,如只能操作具有特定属性的元素,且存在同步操作可能导致页面卡顿的问题。同时,给出了一个实际应用的示例,说明了如何使用`createFakeElement`函数来模拟复制操作。 3. **复制图片功能**: 介绍了如何使用Clipboard API复制图片,包括将图片转换为Base64格式以及在页面上显示复制状态。 4. **封装**: 提供了一个封装了多个剪贴板操作相关功能的函数,包括图片转Base64、转换Base64以及复制文字和图片等,以简化开发者对这些功能的调用。 总的来说,本文详细介绍了Clipboard API在不同场景下的应用,包括复制文字、图片等功能的使用方法和注意事项,并提供了相关的实际应用示例和封装函数,以便开发者更好地集成这些功能到他们的网页应用中。

正文

一. 基于 Clipboard API 复制文字(推荐)

基本概念

Clipboard API 是一组用于在浏览器中操作剪贴板的 JavaScript API,它允许开发者在网页上读取和写入剪贴板内容,实现复制、剪切和粘贴等功能。Clipboard API 提供了一种在网页上读取和写入剪贴板内容的方式,包括文本、图像和其他类型的数据。Clipboard API 适用于需要与用户剪贴板进行交互的网页应用,如实现一键复制、粘贴功能,或者在用户复制特定内容时自动添加额外信息等。

https://developer.mozilla.org/zh-CN/docs/Web/API/Clipboard_API

主要方法

Clipboard API 提供了几个关键的方法来实现剪贴板的读写操作:

  1. navigator.clipboard.writeText(text):将给定的文本复制到剪贴板。这是一个异步方法,会返回一个 Promise 对象,成功时 Promise 会被解析,失败时会被拒绝。
  2. navigator.clipboard.readText():从剪贴板读取文本内容。这也是一个异步方法,返回一个 Promise 对象,解析后提供剪贴板中的文本内容。
  3. navigator.clipboard.write(data):写入更复杂的数据类型到剪贴板,如文件、图像等。data 参数是一个包含 ClipboardItem 对象的数组,每个 ClipboardItem 对象代表剪贴板中的一项数据。这也是一个异步方法,返回一个 Promise 对象。
  4. navigator.clipboard.read():从剪贴板读取更复杂的数据类型,如文件、图像等。这个方法会返回一个 Promise 对象,解析后提供一个包含 ClipboardItem 对象的数组。

使用限制

  • 用户授权:由于安全和隐私的考虑,浏览器在使用 Clipboard API 时通常需要用户授权。例如,在尝试从剪贴板读取或写入数据时,浏览器可能会要求用户明确允许。
  • 安全上下文:Clipboard API 只能在安全的环境中操作剪贴板,如 HTTPS 页面、localhost本机下。
  • 浏览器兼容性:虽然大多数现代浏览器都支持 Clipboard API,但仍有部分旧版浏览器可能不支持。因此,在使用时需要考虑浏览器的兼容性。

实际应用示例

<template>
    <el-button type="primary" @click="handleCopy">复制文本</el-button>
    <div>{{ message }}</div>
</template>
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const message = ref('复制的内容')

const handleCopy = () => {
    navigator.clipboard
        .writeText(message.value)
        .then(() => {
            ElMessage({
                message: '复制成功',
                type: 'success',
            })
        })
        .catch((err) => {
            console.error('复制失败:', err)
            ElMessage({
                message: '复制失败',
                type: 'error',
            })
        })
}
</script>

二、基于 document.execCommand('copy')

document.execCommand('copy') 是一个在网页上执行复制操作的旧式API,属于 Web API 的一部分,用于在不需要用户交互(如点击或按键)的情况下,通过脚本复制文本到剪贴板。然而,这个API在现代Web开发中已经被视为过时(deprecated),并在许多现代浏览器中受到限制或不再支持,尤其是在没有用户明确交互的情况下。

https://developer.mozilla.org/zh-CN/docs/Web/API/Document/execCommand

缺陷

  • 只能操作input, textarea或具有contenteditable属性的元素
  • execCommand 是同步操作,如果复制/粘贴大量数据,可能会导致页面出现卡顿现象,影响用户体验。
  • 它只能将选中的内容复制到剪贴板,无法向剪贴板任意写入内容
  • 有些浏览器还会跳出提示框,要求用户许可,这时在用户做出选择前,页面会失去响应。

实际应用示例

<template>
    <el-button type="primary" @click="handleCopy2">复制文本2</el-button>
    <div>{{ message }}</div>
</template>
<script setup>
import {
    copyText,
    copyImage,
    imageUrlToBase64,
    parseBase64,
} from './common/copy'
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
const message = ref('复制的内容')

const handleCopy2 = () => {
    // 动态创建 textarea 标签
    const textarea = document.createElement('textarea')
    // 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
    textarea.readOnly = 'readonly'
    textarea.style.position = 'absolute'
    textarea.style.left = '-9999px'
    textarea.style.opacity = '0'
    // 将要 copy 的值赋给 textarea 标签的 value 属性
    textarea.value = message.value
    // 将 textarea 插入到 body 中
    document.body.appendChild(textarea)
    // 选中值并复制
    textarea.select()
    const result = document.execCommand('Copy')
    if (result) {
        ElMessage({
            message: '复制成功',
            type: 'success',
        })
    }
    document.body.removeChild(textarea)
}
</script>

说明

clipboard.js 底层也是基于 document.execCommand去实现的

function createFakeElement(value) {
  var isRTL = document.documentElement.getAttribute('dir') === 'rtl';
  var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS

  fakeElement.style.fontSize = '12pt'; // Reset box model

  fakeElement.style.border = '0';
  fakeElement.style.padding = '0';
  fakeElement.style.margin = '0'; // Move element out of screen horizontally

  fakeElement.style.position = 'absolute';
  fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically

  var yPosition = window.pageYOffset || document.documentElement.scrollTop;
  fakeElement.style.top = "".concat(yPosition, "px");
  fakeElement.setAttribute('readonly', '');
  fakeElement.value = value;
  return fakeElement;
}
var fakeCopyAction = function fakeCopyAction(value, options) {
  var fakeElement = createFakeElement(value);
  options.container.appendChild(fakeElement);
  var selectedText = select_default()(fakeElement);
  command('copy');
  fakeElement.remove();
  return selectedText;
};

三、复制图片功能

<template>
    <el-button type="primary" @click="handleCopyImage">复制图片</el-button>
    <div>{{ message }}</div>
</template>
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
const message = ref('复制的内容')

const handleCopyImage = async () => {
    //具体看下面的封装
    await copyImage('https://cn.vitejs.dev/logo-with-shadow.png')
    ElMessage({
        message: '复制成功',
        type: 'success',
    })
}
</script>

四、封装

/**
 * 图片转base64
 * @param {string} 图片地址
 * @returns
 */
export const imageUrlToBase64 = (imageUrl) => {
    return new Promise((resolve, reject) => {
        let image = new Image()
        image.setAttribute('crossOrigin', 'Anonymous')
        image.src = imageUrl
        image.onload = function () {
            const canvas = document.createElement('canvas')
            canvas.width = image.width
            canvas.height = image.height
            const context = canvas.getContext('2d')
            context.drawImage(image, 0, 0, image.width, image.height)
            const base64Str = canvas.toDataURL('image/png')
            resolve(base64Str)
        }
        image.onerror = function (e) {
            reject(e)
        }
    })
}

/**
 * 转换base64
 * @param {string} base64
 * @returns
 */
export function parseBase64(base64) {
    let re = new RegExp('data:(?<type>.*?);base64,(?<data>.*)')
    let res = re.exec(base64)
    if (res) {
        return {
            type: res.groups.type,
            ext: res.groups.type.split('/').slice(-1)[0],
            data: res.groups.data,
        }
    }
}

/**
 * 复制文字
 * @param {string} text  要复制的文本
 * @returns {boolean} true/false
 */
export const copyText = async (text) => {
    if (navigator && navigator.clipboard) {
        await navigator.clipboard.writeText(text)
        return true
    }
    // 动态创建 textarea 标签
    const textarea = document.createElement('textarea')
    // 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
    textarea.readOnly = 'readonly'
    textarea.style.position = 'absolute'
    textarea.style.left = '-9999px'
    textarea.style.opacity = '0'
    // 将要 copy 的值赋给 textarea 标签的 value 属性
    textarea.value = text
    // 将 textarea 插入到 body 中
    document.body.appendChild(textarea)
    // 选中值并复制
    textarea.select()
    const result = document.execCommand('Copy')
    document.body.removeChild(textarea)
    return result
}

/**
 * 复制图片
 * @param {string} imageUrl 图片地址
 * @param {boolean} isBase64 是否是base64
 */
export const copyImage = async (imageUrl, isBase64 = false) => {
    let base64Url = ''
    if (!isBase64) {
        base64Url = await imageUrlToBase64(imageUrl)
    } else base64Url = imageUrl
    const parsedBase64 = parseBase64(base64Url)
    let type = parsedBase64.type
    //将base64转为Blob类型
    let bytes = atob(parsedBase64.data)
    let ab = new ArrayBuffer(bytes.length)
    let ua = new Uint8Array(ab)
    for (let i = 0; i < bytes.length; i++) {
        ua[i] = bytes.charCodeAt(i)
    }
    let blob = new Blob([ab], { type })
    navigator.clipboard.write([new ClipboardItem({ [type]: blob })])
}

与JavaScript系列:JS实现复制粘贴文字以及图片相似的内容:

JavaScript系列:JS实现复制粘贴文字以及图片

目录一. 基于 Clipboard API 复制文字(推荐)基本概念主要方法使用限制实际应用示例二、基于 document.execCommand('copy')缺陷实际应用示例说明三、复制图片功能四、封装 一. 基于 Clipboard API 复制文字(推荐) 基本概念 Clipboard AP

SonarQube系列-架构与外部集成

介绍 Sonar是一个代码质量管理的开源平台,基于Java开发的,用于管理源代码的质量,通过插件形式,可以支持包括java、C#、JavaScript等二十余种编程语言的代码质量管理与检测。 它具有免费的社区版本和其他付费版本。 SonarQube之采购选型参考 利用SonarQube的主要好处是:

详解Web应用安全系列(2)注入漏洞之XSS攻击

上一篇介绍了SQL注入漏洞,今天我们来介绍另一个注入漏洞,即XSS跨站脚本攻击。XSS 全称(Cross Site Scripting) 跨站脚本攻击, 是Web应用中常见的漏洞。指攻击者在网页中嵌入客户端脚本(一般是JavaScript),当用户浏览此网页时,脚本就会在用户的浏览器上执行,从而达到

Semantic Kernel入门系列:利用Handlebars创建Prompts functions

引言 本章我们将学习通过Handlebars Prompts Template来创建Prompts functions。 什么是Handlebars? Handlebars是一个流行的 JavaScript 模板引擎,它允许你通过在 HTML 中使用简单的占位符来创建动态的 HTML。 它使用模板和

JavaScript 如何实现一个响应式系统

JavaScript 如何实现一个响应式系统 第一阶段目标 数据变化重新运行依赖数据的过程 第一阶段问题 如何知道数据发生了变化 如何知道哪些过程依赖了哪些数据 第一阶段问题的解决方案 我们可用参考现有的响应式系统(vue) vue2 是通过 Object.defineProperty实现数据变化的

构建 JavaScript ChatGPT 插件

> 聊天插件系统是一种令人兴奋的新方式,可以扩展ChatGPT的功能,纳入您自己的业务数据,并为客户与您的业务互动增加另一个渠道。在这篇文章中,我将解释什么是聊天插件,它们能做什么,以及你如何用JavaScript建立你自己的聊天插件。 这篇文章(或OpenAI所称的"训练数据")提供了一个快速入门

神奇的JavaScript弱等价类型转换

JavaScript语言特性 - 类型转换 JavaScript这门语言的类型系统从来没有它表面看起来的那样和善,虽然比起Java、C#等一众强类型语言,它的弱类型使用起来似乎是如此便利,但正因为它极高的自由度,所以才会衍生出令人摸不着头脑的荒诞行为。 举个例子,虽然我们都知道一个包含内容的字符串会

[转帖]不同语言的程序在龙芯上的性能表现

https://zhuanlan.zhihu.com/p/591198312 当代的计算机软件都是由高级语言编写的。目前最主流的语言有几个: C/C++:Unix/Linux原生语言,系统级程序,驱动,高性能的应用程序 JAVA:企业级开发主流语言,企业应用后台最常用语言 JavaScript:承载

高级前端开发需要知道的 25 个 JavaScript 单行代码

1. 不使用临时变量来交换变量的值 2. 对象解构,让数据访问更便捷 3. 浅克隆对象 4. 合并对象 5. 清理数组 6. 将 NodeList 转换为数组 7. 检查数组是否满足指定条件 8. 将文本复制到剪贴板 9. 删除数组重复项 10. 取两个数组的交集 11. 求数组元素的总和 12. ...

彻底搞懂JavaScript原型和原型链

基于原型编程 在面向对象的编程语言中,类和对象的关系是铸模和铸件的关系,对象总是从类创建而来,比如Java中,必须先创建类再基于类实例化对象。 而在基于原型编程的思想中,类并不是必须的,对象都是通过克隆另外一个对象而来,这个被克隆的对象就是原型对象。 基于原型编程的语言通常遵循下面的规则: 所有的数