允许用户在浏览器窗口之外共享数据。
您可能听说过 DataTransfer API,它是 HTML5 拖放 API 和剪贴板事件的一部分。它可用于在源目标和接收目标之间转移数据。
拖放和复制粘贴互动通常用于网页内的互动,以将简单文本从 A 转移到 B。但人们往往会忽略,这些互动还可以用于浏览器窗口之外。
浏览器内置的拖放功能和复制粘贴互动可以与其他应用(无论是 Web 应用还是其他应用)进行通信,并且不受任何来源的限制。该 API 支持多个数据条目,并根据数据传输到的位置提供不同的行为。您的 Web 应用可以在监听传入事件时发送和接收传输的数据。
这项功能可能会改变我们对桌面版 Web 应用中分享和互操作性的看法。在应用之间传输数据不再需要依赖紧密耦合的集成。相反,您可以让用户完全掌控数据,并将其转移到任何他们喜欢的位置。
传输数据
首先,您需要实现拖放或复制粘贴功能。以下示例展示了拖放互动,但复制粘贴的过程类似。如果您不熟悉拖放 API,可以参阅这篇介绍 HTML5 拖放功能的精彩文章,其中详细介绍了拖放功能的方方面面。
通过提供以 MIME 类型为键的数据,您可以自由地与外部应用互动。大多数 WYSIWYG 编辑器、文本编辑器和浏览器都会响应以下示例中使用的“原始”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 实例。您会看到,此对象有时会由具有其他名称的属性返回。
接收数据转移的流程与提供数据转移的流程几乎相同。监听接收事件(drop 或 paste)并读取密钥。在拖动到某个元素上时,浏览器只能访问数据的 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元素和富文本 (WYSIWYG) 编辑器(例如 Google 文档、Microsoft Word 等)中呈现 HTML 载荷。text/plain:设置输入元素、代码编辑器内容和来自text/html的回退的值。text/uri-list:当拖放到网址栏或浏览器页面上时,导航到该网址。当放置在目录或桌面上时,系统会创建网址快捷方式。
WYSIWYG 编辑器广泛采用 text/html,使其非常有用。与 HTML 文档一样,您可以使用 Data 网址 或公开可访问的网址来嵌入资源。这非常适合将可视化内容(例如来自画布的可视化内容)导出到 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。请注意,DataTransfer 对象由名为 clipboardData 的属性针对剪贴板事件返回。
// 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 类型开始,也可以使用更贴近您的使用情形的类型,例如 Event、Person、MediaObject、Place,甚至在需要时使用高度特定的类型,例如 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 在 Unsplash 上提供。