運用 DataTransfer API 打破藩籬

讓使用者在瀏覽器視窗外分享資料。

您可能聽過 DataTransfer API,這是 HTML5 Drag and Drop API剪貼簿事件 的一部分。可用於在來源和接收目標之間轉移資料。

瀏覽器支援

  • Chrome:3.
  • Edge:12.
  • Firefox:3.5。
  • Safari:4.

資料來源

拖曳和複製貼上互動功能通常用於在頁面內進行互動,以便將簡單文字從 A 傳送至 B。但我們經常忽略的是,這些互動功能也能用於瀏覽器視窗以外的環境。

瀏覽器內建的拖曳和複製貼上互動功能,都能與其他應用程式 (網頁或其他) 通訊,且不受任何來源限制。API 支援多個資料項目,並根據資料轉移位置提供不同的行為。在監聽傳入事件時,您的網頁應用程式可以傳送及接收傳輸的資料。

這項功能可改變我們在電腦上使用網頁應用程式時,對共用和互通性的看法。在應用程式之間轉移資料時,不再需要仰賴緊密整合。您可以讓使用者自行決定要將資料轉移至何處。

以下範例說明 DataTransfer API 可支援的互動方式。(影片不含音訊)。

轉移資料

如要開始使用,您必須實作拖曳或複製貼上功能。以下範例顯示拖曳互動,但複製貼上程序類似。如果您不熟悉拖曳放置 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 的例項。您會發現,有時屬性會以其他名稱傳回這個物件。

接收資料移轉的運作方式幾乎與提供資料移轉相同。監聽接收事件 (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/htmlcontentEditable 元素和 Google 文件、Microsoft Word 等富文字 (WYSIWYG) 編輯器中轉譯 HTML 酬載。
  • 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 類型開始,或是使用更符合用途的類型,例如 EventPersonMediaObjectPlace,甚至是 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 也支援複製貼上。請務必監聽複製貼上事件。這項作業不需要花費太多額外心力,而且使用者也會感謝您新增這項功能。

安全性和隱私權

使用這項技術時,請注意一些安全性和隱私權考量事項。

  • 使用者裝置上的其他應用程式可存取剪貼簿資料。
  • 您要拖曳的網頁應用程式可存取類型鍵,但無法存取資料。資料只有在放置或貼上時才會顯示。
  • 收到的資料應視為其他使用者輸入內容,使用前請先清理及驗證。

開始使用 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