運用 DataTransfer API 打破藩籬

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

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

瀏覽器支援

  • 3
  • 12
  • 3.5
  • 4

資料來源

使用拖曳和複製貼上互動功能常用於頁面中的互動,將簡單的文字從 A 轉移到 B。但最常見的情況是,您可以利用這些互動操作,跳脫瀏覽器視窗以外的地方。

瀏覽器內建的拖曳功能和複製貼上互動都可以與其他應用程式、網路或其他方式通訊,也不會與任何來源建立關聯。這個 API 可根據資料傳輸位置,支援多個資料項目,且行為各有不同。網頁應用程式可在監聽傳入事件時,傳送及接收已轉移的資料。

這項功能可改變我們對電腦版網頁應用程式的共用和互通性的看法。在應用程式之間轉移資料,不再需要採用緊耦合的整合方式。相反地,您可以讓使用者完全掌控 將資料轉移至任何位置

可透過 DataTransfer API 進行的互動範例。(影片不含音效)。

轉移資料

請先導入拖曳或複製貼上功能。以下範例顯示拖曳互動,但複製貼上的程序很類似。如果您不熟悉 Drag and Drop API,可以參考這篇說明的 HTML5 拖曳說明文章,進一步瞭解相關細節。

提供 MIME-type 金鑰資料,您可以自由與外部應用程式互動。大多數 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 元素和 Rich Text (WYSIWYG) 編輯器 (例如 Google 文件、Microsoft Word 等) 中顯示 HTML 酬載。
  • text/plain: 設定輸入元素的值、程式碼編輯器的內容,以及來自 text/html 的備用設定。
  • text/uri-list拖曳網址列或瀏覽器頁面時,前往網址。拖放目錄或桌面時,系統會建立網址捷徑。

text/html」是 WYSIWYG 編輯器廣泛採用,因此非常實用。與 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 與複製貼上互動互動。請注意,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 類型著手,也可以視需要使用更靠近用途的類型,例如 EventPersonMediaObjectPlace,甚至是 MedicalEntity 等高度具體類型。使用 TypeScript 時,您可以使用 schema-dts 類型定義的介面定義。

傳輸和接收 JSON-LD 資料,可使網路世界變得更加緊密連結,也更開放。當應用程式使用相同語言時,您就可以與外部應用程式建立深度整合。您不需要進行複雜的 API 整合作業,所有必要資訊都會包含在轉移的資料中。

思考在任何沒有限制的情況下,在任何 (網頁應用程式) 應用程式之間轉移資料的可能性:將日曆中的活動分享至喜愛的 ToDo 應用程式、將虛擬檔案附加至電子郵件、分享聯絡人。這樣很好,對吧?一切從你開始!🙌

疑慮

雖然 DataTransfer API 目前可供使用,但在整合之前,請注意以下事項。

瀏覽器相容性

電腦版瀏覽器都完整支援上述技術,行動裝置則無法。這項技術已在所有主要瀏覽器 (Chrome、Edge、Firefox、Safari) 和作業系統 (Android、ChromeOS、iOS、macOS、Ubuntu Linux 和 Windows) 上完成測試,但很抱歉,Android 和 iOS 並未通過測試。雖然瀏覽器會持續開發,但目前這項技術僅適用於電腦版瀏覽器。

Shorts 曝光機制

使用拖曳和複製貼上功能在桌上型電腦工作時,屬於系統層級的互動,根層級可追溯至 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 ErtelUnsplash 上提供的主頁橫幅。