Skip to content

控制粘贴板

介绍

控制粘贴板,无非就两种 Web API 实现

  • document.execCommand
  • navigator.clipboard

区别:

  1. 兼容性

    • document.execCommand是旧的操作粘贴板API,随时可能被废弃
    • navigator.clipboard是用来替代document.execCommand的,在旧版本浏览器也许不能使用
  2. 同步和异步

    • document.execCommand是同步执行
    • navigator.clipboard是异步执行
    • 复制粘贴大量数据时,document.execCommand不友好,因为同步,可能会导致页面卡住
  3. 剪贴板权限

    • document.execCommand不需要请求剪贴板权限,注意某些浏览器调用 document.execCommand 需要用户先有操作,就像音频自动播放一样,要先有点击等操作,才能调用成功
    • navigator.clipboard需要用户受授剪贴板数据
  4. 写入获取

    • document.execCommand 只能写入,已经不能获取了

      js
      document.execCommand('copy') // true or false
      document.execCommand('paste') // false,永远为 false
    • navigator.clipboard 在获取剪贴板权限后,可以写入获取

写入文本到粘贴板

typescript
function copyText(text: string, success?: () => void, error?: () => void) {
  if (document.execCommand('copy')) {
    try {
      // 创建一个文本框,并把 innerHTML 换成要写入的文本
      const txa = document.createElement('textarea')
      txa.innerHTML = text
      document.body.appendChild(txa)
      // 选中文本
      txa.select()
      // 复制
      const res = document.execCommand('copy')
      // 从文档中移除
      document.body.removeChild(txa)
      if (res) {
				success && success()
      	return
      }
    } catch(err) {
      console.log(err)
      error && error()
    }
  }

  if (navigator.clipboard) {
    // 直接调用API即可
    navigator.clipboard.writeText(text).then(success).catch(error)
  } else {
    error && error()
  }
}

写入图片到粘贴板

typescript
function copyImage(url: string, success?: () => void, error?: () => void) {
  const img = new Image()
  img.src = url
  img.crossOrigin = ''

  img.onerror = () => {
    error && error()
  }

  img.onload = () => {
    document.body.appendChild(img)

    if (document.execCommand('copy')) {
      // 使用 Selection + Range 选中图片这个 dom
      const selection = window.getSelection()
      if (selection) {
        selection.removeAllRanges()
        const range = document.createRange()
        range.selectNode(img)
        selection.addRange(range)
        // 复制
        const res = document.execCommand('copy')
        document.body.removeChild(img)
        // 把所有选中去掉
        selection.removeAllRanges()
        if (res) {
          success && success()
          return
        }
      }
    }

    if (navigator.clipboard) {
      // navigator.clipboard 只能复制 png 类型的图片
      // 所以考虑 url 可能是除 png 外的图片,需要用 canvas 转一次
      const canvas = document.createElement('canvas')
      canvas.width = img.width
      canvas.height = img.height
      const ctx = canvas.getContext('2d')
      if (ctx) {
        ctx.drawImage(img, 0, 0)
        document.body.removeChild(img)
        canvas.toBlob((blob) => {
          if (blob) {
            // 调用 write,写入 blob 图片数据
            navigator.clipboard.write([
              new ClipboardItem({
                'image/png': blob
              })
            ]).then(success).catch(error)
          } else {
            error && error()
          }
        })
      } else {
        error && error()
      }
    } else {
      error && error()
    }
  }
}

获取粘贴板数据

typescript
function readClipboard(success: (text: string) => void, error?: () => void) {
  if (navigator.clipboard) {
    navigator.clipboard.readText().then(success).catch(error)
  } else {
    error && error()
  }
}