在浏览器中取消阻止剪贴板访问

发布时间:2021-01-18 13:46:21 阅读:29

在过去的几年中,浏览器已用于 document.execCommand() 剪贴板交互。尽管得到了广泛的支持,但这种剪切和粘贴方法还是要付出代价的:剪贴板访问是同步的,并且只能读写 DOM。

这对于一小段文字来说很好,但是在很多情况下,阻止页面进行剪贴板传输是一种糟糕的体验。在内容可以安全粘贴之前,可能需要耗时的清理或图像解码。浏览器可能需要从粘贴的文档中加载或内联链接资源。在磁盘或网络上等待时会阻塞页面。想象一下,将权限添加到混合中,要求浏览器在请求剪贴板访问时阻止页面。同时,document.execCommand()用于剪贴板交互的权限 被松散定义,并且在浏览器之间有所不同。

在 异步剪贴板 API 解决了这些问题,提供了一个良好定义的权限模型不阻止页面。Safari 最近宣布 在 13.1 版中支持它。这样,主要的浏览器就具有基本的支持水平。在撰写本文时,Firefox 仅支持文本。在某些浏览器中,图像支持仅限于 PNG。如果你对使用 API感兴趣, 请 在继续之前查阅浏览器支持表。

异步剪贴板 API 仅限于处理文本和图像。Chrome 84 引入了一项实验性功能,该功能使剪贴板可以处理任意数据类型。

复制:将数据写入到剪贴板

writeText()

要将文本复制到剪贴板,请调用writeText()。由于此 API 是异步的,因此该writeText()函数将返回一个 Promise,该 Promise 将根据传递的文本是否被成功复制来解决或拒绝:

async function copyPageUrl() {
  try {
    await navigator.clipboard.writeText(location.href);
    console.log('Page URL copied to clipboard');
  } catch (err) {
    console.error('Failed to copy: ', err);
  }
} 

write()

实际上,writeText()这只是通用write() 方法的一种便捷方法,它还允许你将图像复制到剪贴板。像一样writeText(),它是异步的并返回 Promise。

要将图像写入剪贴板,你需要将图像作为 blob。一种实现方法是使用fetch(),从服务器请求图像,然后调用 blob()响应。

由于多种原因,可能不希望或不可能从服务器请求图像。幸运的是,你还可以将图像绘制到画布上并调用 canvas 的 toBlob() 方法。

接下来,将ClipboardItem对象数组作为参数传递给write() 方法。目前,你一次只能传送一张图片,但我们希望将来增加对多张图片的支持。ClipboardItem将图像的 MIME 类型作为键并将 Blob 作为值的对象。对于从fetch()或获取的 Blob 对象canvas.toBlob(),该blob.type属性自动包含图像的正确 MIME 类型。

try {
  const imgURL = '/images/generic/file.png';
  const data = await fetch(imgURL);
  const blob = await data.blob();
  await navigator.clipboard.write([
    new ClipboardItem({
      [blob.type]: blob
    })
  ]);
  console.log('Image copied.');
} catch (err) {
  console.error(err.name, err.message);
} 

复制事件

在用户启动剪贴板副本的情况下,非文本数据将作为 Blob 提供给你。该 copy事件 包括一个clipboardData属性,该属性具有已采用正确格式的项目,从而无需手动创建 Blob。呼叫preventDefault()以防止默认行为因你自己的逻辑而异,然后将内容复制到剪贴板。在此示例中未涵盖的是当不支持剪贴板 API 时如何回退到较早的 API。

document.addEventListener('copy', async (e) => {
    e.preventDefault();
    try {
      let clipboardItems = [];
      for (const item of e.clipboardData.items) {
        if (!item.type.startsWith('image/')) {
          continue;
        }
        clipboardItems.push(
          new ClipboardItem({
            [item.type]: item,
          })
        );
        await navigator.clipboard.write(clipboardItems);
        console.log('Image copied.');
      }
    } catch (err) {
      console.error(err.name, err.message);
    }
  }); 

粘贴:从剪贴板中读取数据

readText()

要从剪贴板中读取文本,请致电navigator.clipboard.readText()并等待返回的 Promise 解决:

async function getClipboardContents() {
  try {
    const text = await navigator.clipboard.readText();
    console.log('Pasted content: ', text);
  } catch (err) {
    console.error('Failed to read clipboard contents: ', err);
  }
} 

read()

navigator.clipboard.read()方法也是异步的,并返回 Promise。要从剪贴板读取图像,请获取 ClipboardItem 对象列表 ,然后对其进行迭代。

每个都ClipboardItem可以将其内容保留为不同的类型,因此你需要再次使用for...of循环来遍历类型列表。对于每种类型,请getType()以当前类型为参数调用该方法以获得相应的 Blob。和以前一样,此代码不与图像绑定,并且可以与其他将来的文件类型一起使用。

async function getClipboardContents() {
  try {
    const clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      for (const type of clipboardItem.types) {
        const blob = await clipboardItem.getType(type);
        console.log(URL.createObjectURL(blob));
      }
    }
  } catch (err) {
    console.error(err.name, err.message);
  }
} 

paste 事件

如前所述,已经计划引入事件以与 Clipboard API 一起使用,但是现在你可以使用现有paste事件。它与用于读取剪贴板文本的新异步方法很好地配合使用。与copy活动一样,不要忘记致电preventDefault()

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  const text = await navigator.clipboard.readText();
  console.log('Pasted text: ', text);
}); 

处理多种文件类型

大多数实现将多种数据格式放在剪贴板上,以便进行单个剪切或复制操作。这样做的原因有两个:作为应用程序开发人员,你无法了解用户要将文本或图像复制到的应用程序的功能,并且许多应用程序都支持将结构化数据粘贴为纯文本。这是通过 “编辑” 菜单项呈现给用户的,该菜单项的名称为 “粘贴和匹配样式” 或 “不格式化”

以下示例显示了如何执行此操作。此示例用于fetch()获取图像数据。

function copy() {
  const image = await fetch('kitten.png');
  const text = new Blob(['Cute sleeping kitten'], {type: 'text/plain'});
  const item = new ClipboardItem({
    'text/plain': text,
    'image/png': image
  });
  await navigator.clipboard.write([item]);
} 

安全性和权限

剪贴板访问一直是浏览器的安全隐患。没有适当的权限,页面可能会以静默方式将所有形式的恶意内容复制到用户的剪贴板,粘贴时会产生灾难性的结果。

剪贴板 API 的权限提示

使网页不受限制地对剪贴板具有读取权限,这会更加麻烦。用户通常将诸如密码和个人详细信息之类的敏感信息复制到剪贴板,然后在用户不知情的情况下,任何页面都可以读取这些信息。

与许多新 API 一样,剪贴板 API 仅支持通过 HTTPS 服务的页面。为防止滥用,仅当页面为活动选项卡时才允许剪贴板访问。活动选项卡中的页面可以在不请求权限的情况下写入剪贴板,但是从剪贴板中读取始终需要权限。

复制和粘贴的权限已添加到 Permissions API 中。clipboard-write当页面是活动选项卡时,权限会自动授予它们。clipboard-read必须请求该权限,你可以通过尝试从剪贴板读取数据来执行此操作。下面的代码显示了后者:

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

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

你还可以使用该allowWithoutGesture选项控制是否需要用户手势来调用剪切或粘贴。该值的默认值因浏览器而异,因此应始终将其包括在内。

这就是剪贴板 API 的异步特性真正派上用场的地方:尝试读取或写入剪贴板数据会自动提示用户获得许可(如果尚未授予许可)。由于 API 基于承诺,因此这是完全透明的,并且用户拒绝剪贴板权限会导致承诺被拒绝,因此页面可以适当地响应。

由于 Chrome 仅在页面为活动标签时才允许剪贴板访问,因此你会发现,如果直接粘贴到 DevTools 中,此处的某些示例将不会运行,因为 DevTools 本身就是活动标签。有一个技巧:使用延迟剪贴板访问setTimeout(),然后在调用函数之前快速在页面内部单击以将其聚焦:

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

特征检测

要在支持所有浏览器的同时使用 Async Clipboard API,请测试 navigator.clipboard并退回到较早的方法。例如,这是实现粘贴以包括其他浏览器的方式。

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

这还不是全部。在 Async Clipboard API 之前,Web 浏览器混合了多种复制和粘贴实现。在大多数浏览器中,可以使用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.');
  }
}); 

我们在微信上24小时期待你的声音
解答:网站优化,网站建设,搜索引擎优化,APP 开发,小程序开发

非常感谢您有耐心的读完这篇文章:"在浏览器中取消阻止剪贴板访问",此文章仅为提供更多信息供用户参考使用或为学习交流的方便。如果对您有帮助,请收藏我们的网址:https://www.91webs.cn


18617670560