异步剪贴板API:解放剪贴板操作

异步剪贴板API:解放剪贴板操作

技术杂谈小彩虹2021-08-19 2:30:07130A+A-

本文是谷歌开发者网站近期发布的关于异步剪贴板API使用方式的指导文章。

原文(需越墙)developers.google.com/web/updates…

原文作者Jason Miller  译者西楼听雨
(转载请注明出处)

在过去的几年里,对于与剪贴板交互的支持,各家浏览器都在往 document.execCommand 聚集。虽然这种统一的、广泛的支持是一件好事,但是这里还有一个问题:这种剪贴板的操作方式是同步性的,而且只能读写 DOM 上的内容。

同步式的复制、粘贴对于短小的文本来说看上去还没问题,但其实还有许多应用场景是会由于这种阻塞页面的操作带来糟糕用户体验的。例如,在富文本内容可以被粘贴进来时,浏览器需要先进行耗时的内容转换、格式清理工作,或者图片解压工作;而对于外部资源,浏览器可能还需要先进行加载——这样就会阻塞页面,直到资源从硬盘或者网络加载完成;如果再在这些过程中添加权限控制,则会导致更严重的阻塞,因为需要先请求用户授予应用访问剪贴板的权限。

当下,与document.execCommand中剪贴板交互相关的权限,还没有精细的定义,各家浏览器之间也不尽相同。所以,如果我们希望有一个能专门解决阻塞、权限问题的剪贴板 API 出现,它应该是什么样的呢?

新制定的“异步剪贴板API”就是我们想要的,它对相关权限定义了一个优良的模型,也不会阻塞页面,同时简化了剪贴板相关的事件,并且与“拖放API(Drag & Drop API)”进行了打通。这个新的 API 将在 Chrome 66 中发布。

视频演示: v.youku.com/v_show/id_X…

复制:向剪贴板写入文本

文本内容可以通过 writeText() 写入剪贴板。因为这个 API 是异步式的,所以这个方法会返回一个 Promise,成功或者失败则基于我们传入的文本是否复制成功:

navigator.clipboard.writeText('Text to be copied')
  .then(() => {
    console.log('文本已复制到了剪贴板');
  })
  .catch(err => {
    // 当用户拒绝剪贴板写入操作的权限请求时,就会失败
    console.error('无法复制文本: ', err);
  });

我们也可以用异步函数的方式来实现,然后通过 await 关键字来返回 writeText() 的结果:

async function copyPageUrl() {
  try {
    await navigator.clipboard.writeText(location.href);
    console.log('页面URL复制成功');
  } catch (err) {
    console.error('复制失败: ', err);
  }
}

粘贴:从剪贴板读取文本

和复制操作非常类似,文本内容可以通过调用 readText() 从剪贴板读取:

navigator.clipboard.readText()
  .then(text => {
    console.log('剪贴板内容: ', text);
  })
  .catch(err => {
    console.error('读取剪贴板失败: ', err);
  });

异步函数实现:

async function getClipboardContents() {
  try {
    const text = await navigator.clipboard.readText();
    console.log('剪贴板内容: ', text);
  } catch (err) {
    console.error('读取剪贴板失败: ', err);
  }
}

处理粘贴事件

对于剪贴板内容的变动检测,已经有计划为其引入一个事件了,但是现在最好是通过"paste"事件来实现检测,它可以和这个新的 API 很好地配合使用:

document.addEventListener('paste', event => {
  event.preventDefault();
  navigator.clipboard.getText().then(text => {
    console.log('Pasted text: ', text);
  });
});

安全性和权限

剪贴板访问的安全性一直是浏览器的一个关注点,如果没有在关键的地方进行恰当的权限控制,页面可能会不知不觉地复制各种形式的恶意内容到用户的剪贴板,进而带来灾难性的后果。例如,想象这样一个Web页面,它不知不觉地复制了“rm -rf /”这个命令,或者复制了一张“解压炸弹图片(decompression bomb image)”(译注:一种攻击形式,利用了内容重复的超大尺寸图片压缩后可以变得非常小的特性;因浏览器渲染时需要先解压,这个过程就会耗用巨大的内存和性能)。

给予 Web 页面不受限制的读取权限,甚至可能会更糟糕。例如,当用户复制了敏感信息,比如,密码、个人信息时,任何页面都可以在用户不知情的情况下进行读取。

所以,和其他许多近来新出现的 API 一样,navigator.clipboard 也仅仅在 HTTPS 下才可用。而且为了防止滥用,剪贴板的访问仅在页面所在标签处于激活状态时才被允许。其中剪贴板的写入在页面标签为激活状态时不需要请求权限,而读取则在任何情况下都需要权限。

为了让事情变得容易,和复制、粘贴相关的两种权限项被添加到了权限API(Permissions API)中。一个是 clipboard-write,页面标签在激活状态时,会自动获得该权限;一个是clipboard-read,该权限可以通过尝试从剪贴板读取数据来请求。

{ name: 'clipboard-read' }
{ name: 'clipboard-write' }

Screenshot
of the permissions prompt shown when attempting to read from the clipboard.

和其他任何使用权限 API 的情况一样, 可以通过它检查你的应用是否有和剪贴板交互的权限:

navigator.permissions.query({
  name: 'clipboard-read'
}).then(permissionStatus => {
  // Will be 'granted', 'denied' or 'prompt':
  console.log(permissionStatus.state);

  // Listen for changes to the permission state
  permissionStatus.onchange = () => {
    console.log(permissionStatus.state);
  };
});

异步剪贴板API中的“异步”特性有其非常方便好用的一面,例如:在用户还没授予权限之前,你尝试对剪贴板进行读或写操作,会自动帮你提示用户进行授权;又如:因为这个 API 是基于 promise 的,所以这个授权的过程是完全透明的;而且当用户拒绝了剪贴板访问请求时,页面也有机会做出相应的响应。

因为只有页面是在激活状态时才可以进行剪贴板访问,而 Chrome 的开发者工具本身也是标签,所以在打开了开发者工具时,某些情况下可能就会出现不正常现象。对于这个问题,这里有个技巧:我们可以通过 setTimeout 来推迟对剪贴板的访问,然后再在回调函数执行之前,快速点击页面,把焦点切换进去:

setTimeout(async () => {
  const text = await navigator.clipboard.readText();
  console.log(text);
}, 2000);

历史回顾

在引入异步剪贴板 API 之前,我们会采用了一种混合了不同复制、粘贴实现方法的方案,来支持各家浏览器。

但在大多数浏览器中,他们的复制、粘贴操作都是通过document.execCommand('copy')document.execCommand('paste')来执行的。如果被拷贝的文本不在 DOM 中,我们还需要先手动把它注入到 DOM 中然后选择它:

button.addEventListener('click', e => {
  const input = document.createElement('input');
  document.body.appendChild(input);
  input.value = text;
  input.focus();
  input.select();
  const result = document.execCommand('copy');
  if (result === 'unsuccessful') {
    console.error('Failed to copy text.');
  }
})

对于,在不支持异步剪贴板API的浏览器中处理粘贴操作,则是这样的:

document.addEventListener('paste', e => {
  const text = e.clipboardData.getData('text/plain');
  console.log('Got pasted text: ', text);
})

而在 IE 中,我们可以通过 window.clipboardData 来访问剪贴板。而且如果是在一个用户手势事件——如点击事件——中访问的,那么权限请求的对话框就不会弹出——asking permission responsibly(译注:因为这其实相当于一种已经获得了用户默许的环境)。

特性检测及备用方案

在利用异步剪贴板API好处的同时也支持其他不支持这个 API 的浏览器,是一个不错的方案。要实现这种效果,你可以通过检查navigator.clipboard属性的存在性来判断异步剪贴API是否受支持:

document.addEventListener('paste', async e => {
  let text;
  if (navigator.clipboard) {
    text = await navigator.clipboard.readText()
  }
  else {
    text = e.clipboardData.getData('text/plain');
  }
  console.log('Got pasted text: ', text);
});

接下来还有什么

通篇下来,你可能注意到了,我们仅涉及到了 navigator.clipboard 文本操作的部分,其实,这个 API 规范还定义了一些更通用的 read() 和 write() 方法,但是实现这些方法存在复杂性和安全性问题(还记得“图片炸弹”吗?)。所以,Chrome 现在只释放了这个 API 中和文本相关的一些部分。

更多信息


点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们