使用 DataTransfer API 打破障碍

允许用户在浏览器窗口之外分享数据。

您可能听说过 DataTransfer API, 部分 HTML5 拖放 API剪贴板事件。它可以 用于在源目标和接收目标之间传输数据。

浏览器支持

  • Chrome:3. <ph type="x-smartling-placeholder">
  • Edge:12。 <ph type="x-smartling-placeholder">
  • Firefox:3.5。 <ph type="x-smartling-placeholder">
  • Safari:4. <ph type="x-smartling-placeholder">

来源

拖放和复制粘贴互动通常用于页面内的互动 将简单文本从 A 传输到 B。但人们经常忽视了 将这些相同的互动移出浏览器窗口。

浏览器内置的拖放操作和复制粘贴互动都可以 不依赖于任何源。该 API 支持多种 具有不同行为的数据条目。您的 Web 应用在监听传入事件时可以发送和接收传输的数据。

此功能可能会改变我们对网络共享和互操作性的思考 。在应用之间传输数据并不需要依赖 紧密耦合的集成。不过,您可以向用户提供转移数据的完全控制权 随时随地访问数据

<ph type="x-smartling-placeholder">
</ph>
可使用 DataTransfer API 进行的互动示例。(视频没有声音。)

传输数据

首先,您需要实现拖放或复制粘贴功能。示例 下面显示的是拖放互动,但复制和粘贴的过程与此类似。如果 您不熟悉 Drag and Drop API,但是,以下一篇非常实用的文章 HTML5 拖放说明:介绍了输入和输出内容。

通过提供 MIME 类型键控数据, 您可以自由地与外部应用交互。 大多数所见即所得编辑器、文本编辑器和浏览器都对“原语”MIME 类型 示例。

document.querySelector('#dragSource')
.addEventListener('dragstart', (event) => {
  event.dataTransfer.setData('text/plain', 'Foo bar');
  event.dataTransfer.setData('text/html', '<h1>Foo bar</h1>');
  event.dataTransfer.setData('text/uri-list', 'https://example.com');
});

请注意 event.dataTransfer 属性。这将返回一个 DataTransfer。如 有时由具有其他名称的属性返回该对象。

接收数据传输的方式与提供数据传输的方式几乎相同。监听接收事件 (droppaste)并读取键。在元素上拖动元素时,浏览器只能访问 数据的 type 键。数据本身只能在拖放后访问。

document.querySelector('#dropTarget')
.addEventListener('dragover', (event) => {
  console.log(event.dataTransfer.types);
  // Without this, the drop event won't fire.
  event.preventDefault();
});

document.querySelector('#dropTarget')
.addEventListener('drop', (event) => {
  // Log all the transferred data items to the console.
  for (let type of event.dataTransfer.types) {
    console.log({ type, data: event.dataTransfer.getData(type) });
  }
  event.preventDefault();
});

应用广泛支持三种 MIME 类型:

  • text/html:以 contentEditable 元素的形式呈现 HTML 载荷, 文本(所见即所得)编辑器,如 Google 文档、Microsoft Word 等。
  • text/plain: 设置输入元素的值、代码编辑器的内容和后备元素 text/html起。
  • text/uri-list:将鼠标拖放到网址栏或浏览器页面上时,会转到相应网址。网址 。

由于 WYSIWYG 编辑器广泛采用 text/html,因此它非常有用。与在 HTML 中一样 你可以使用 数据网址或公开网址 可访问的网址这非常适合将可视化内容(例如从画布上导出)导出到 Google 文档。

const redPixel = 'data:image/gif;base64,R0lGODdhAQABAPAAAP8AAAAAACwAAAAAAQABAAACAkQBADs=';
const html = '<img src="' + redPixel + '" width="100" height="100" alt="" />';
event.dataTransfer.setData('text/html', html);

使用复制和粘贴功能转移

使用 DataTransfer API 进行复制和粘贴互动如下所示。请注意, 针对剪贴板事件,名为 clipboardData 的属性会返回 DataTransfer 对象。

// Listen to copy-paste events on the document.
document.addEventListener('copy', (event) => {
  const copySource = document.querySelector('#copySource');
  // Only copy when the `activeElement` (i.e., focused element) is,
  // or is within, the `copySource` element.
  if (copySource.contains(document.activeElement)) {
    event.clipboardData.setData('text/plain', 'Foo bar');
    event.preventDefault();
  }
});

document.addEventListener('paste', (event) => {
  const pasteTarget = document.querySelector('#pasteTarget');
  if (pasteTarget.contains(document.activeElement)) {
    const data = event.clipboardData.getData('text/plain');
    console.log(data);
  }
});

自定义数据格式

您不仅限于原始 MIME 类型,但可以使用任何键来标识传输 数据。这对于应用内的跨浏览器交互非常有用。如下所示, 可以使用 JSON.stringify()JSON.parse() 函数传输更复杂的数据。

document.querySelector('#dragSource')
.addEventListener('dragstart', (event) => {
  const data = { foo: 'bar' };
  event.dataTransfer.setData('my-custom-type', JSON.stringify(data));
});

document.querySelector('#dropTarget')
.addEventListener('dragover', (event) => {
  // Only allow dropping when our custom data is available.
  if (event.dataTransfer.types.includes('my-custom-type')) {
    event.preventDefault();
  }
});

document.querySelector('#dropTarget')
.addEventListener('drop', (event) => {
  if (event.dataTransfer.types.includes('my-custom-type')) {
    event.preventDefault();
    const dataString = event.dataTransfer.getData('my-custom-type');
    const data = JSON.parse(dataString);
    console.log(data);
  }
});

连接网络

虽然自定义格式非常适合由您控制的应用之间的通信, 在向没有使用您格式的应用程序传输数据时,还会对用户进行限制。如果您想 与网络上的第三方应用连接,则需使用通用数据格式。

JSON-LD(关联数据)标准非常适合实现这一目的。时间是 轻量级且易于使用 JavaScript 读写。Schema.org 包含许多 预定义类型可用,您也可以选择自定义架构定义。

const data = {
  '@context': 'https://schema.org',
  '@type': 'ImageObject',
  contentLocation: 'Venice, Italy',
  contentUrl: 'venice.jpg',
  datePublished: '2010-08-08',
  description: 'I took this picture during our honey moon.',
  name: 'Canal in Venice',
};
event.dataTransfer.setData('application/ld+json', JSON.stringify(data));

使用 Schema.org 类型时,您可以从通用 Thing 类型开始, 或使用更接近您的用例的内容,例如事件人物MediaObjectPlace 甚至是非常具体的类型,例如 MedicalEntity(如果需要)。使用 TypeScript 时,你可以使用 schema-dts 类型定义中的接口定义。

通过传输和接收 JSON-LD 数据,您将能支持更紧密、更开放的网络。包含 因此您可以创建与外部 应用。无需进行复杂的 API 集成;所需的全部信息 包含在传输的数据中。

想想无需 限制:将日历中的活动分享到您喜爱的待办事项应用,将虚拟文件附加到 电子邮件、共享联系人。那就太好了,对吧?从您开始!🙌

问题

虽然 DataTransfer API 现在已经可以使用,但在集成之前需要注意一些事项。

浏览器兼容性

桌面浏览器都支持上述技术,而移动设备则支持 错误。此技术在所有主流浏览器(Chrome、Edge、Firefox、Safari)上都进行了测试, 操作系统(Android、ChromeOS、iOS、macOS、Ubuntu Linux 和 Windows),但遗憾的是,Android 而 iOS 未通过测试。虽然浏览器仍在继续开发,但目前,该技术还存在一定局限性 仅适用于桌面浏览器。

曝光度

拖放和复制和粘贴是在桌面设备上工作时的系统级互动, 可以追溯到 40 年前的第一个 GUI。想想您有多少次 并利用这些互动整理文件这种情况在网络中并不常见。

您需要向用户介绍这种新的互动方式,并想出相应的用户体验模式 这对于那些目前只使用移动设备的计算机用户而言尤其如此。

无障碍

拖放并不是非常易于访问的互动,但 DataTransfer API 也支持复制粘贴。 请务必监听复制粘贴事件。使用这项功能不需要太多额外工作,而且您的用户 会感谢您添加该代码。

安全和隐私设置

在使用该方法时,有一些安全和隐私注意事项。

  • 剪贴板数据可供用户设备上的其他应用访问。
  • 您拖动到的 Web 应用可以访问类型键,但无法访问数据。仅 拖放或粘贴时可用。
  • 接收到的数据应像处理任何其他用户输入一样处理;在使用前需要清理和验证。

Transmat 帮助程序库使用入门

您是否有兴趣在您的应用中使用 DataTransfer API?您可以考虑查看 GitHub 上的 Transmat 库。这个开源库使浏览器与 不同之处,提供了 JSON-LD 实用程序,包含一个可响应传输事件 突出显示拖放区域,并支持将数据传输操作与现有拖放区域集成 实现。

import { Transmat, TransmatObserver, addListeners } from 'transmat';

// Send data on drag/copy.
addListeners(myElement, 'transmit', (event) => {
  const transmat = new Transmat(event);
  transmat.setData({
    'text/plain': 'Foobar',
    'application/json': { foo: 'bar' },
  });
});

// Receive data on drop/paste.
addListeners(myElement, 'receive', (event) => {
  const transmat = new Transmat(event);
  if (transmat.hasType('application/json') && transmat.accept()) {
    const data = JSON.parse(transmat.getData('application/json'));
  }
});

// Observe transfer events and highlight drop areas.
const obs = new TransmatObserver((entries) => {
  for (const entry of entries) {
    const transmat = new Transmat(entry.event);
    if (transmat.hasMimeType('application/json')) {
      entry.target.classList.toggle('drag-over', entry.isTarget);
      entry.target.classList.toggle('drag-active', entry.isActive);
    }
  }
});
obs.observe(myElement);

致谢

主打图片,作者:Luba Ertel 不启动