DataTransfer API を使用して障壁を取り払う

ユーザーがブラウザ ウィンドウの外でデータを共有できるようにします。

DataTransfer API は、HTML5 ドラッグ&ドロップ APIClipboard イベントの一部です。これは、送信元と受信先のターゲット間でデータを転送するために使用できます。

Browser Support

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

Source

ドラッグ&ドロップやコピー&ペーストの操作は、ページ内の操作で 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 のインスタンスが返されます。ご覧のとおり、このオブジェクトは他の名前のプロパティによって返されることもあります。

データ転送の受信は、データの提供とほぼ同じように機能します。受信イベント(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();
});

次の 3 つの MIME タイプは、アプリケーション全体で幅広くサポートされています。

  • text/html: contentEditable 要素と、Google ドキュメントや Microsoft Word などのリッチテキスト(WYSIWYG)エディタで HTML ペイロードをレンダリングします。
  • text/plain:: 入力要素の値、コードエディタのコンテンツ、text/html からのフォールバックを設定します。
  • text/uri-list: URL バーまたはブラウザページにドロップすると、URL に移動します。ディレクトリまたはデスクトップにドロップすると、URL ショートカットが作成されます。

WYSIWYG エディタで text/html が広く採用されているため、非常に便利です。HTML ドキュメントと同様に、データ URL または一般公開されている URL を使用してリソースを埋め込むことができます。これは、ビジュアル(キャンバスなど)を 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(Linked Data)標準が最適です。軽量で、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 はテストに合格しませんでした。ブラウザは進化を続けていますが、今のところこの手法はデスクトップ ブラウザでのみ利用できます。

見つけやすさ

ドラッグ&ドロップとコピー&ペーストは、デスクトップ パソコンで作業する際のシステムレベルの操作であり、40 年以上前の最初の GUI にまで遡ります。これらの操作をファイルの整理に何回使用したか考えてみましょう。ウェブではまだ一般的ではありません。

ユーザーにこの新しい操作について説明し、特にモバイル デバイスしか使ったことがないユーザーにもわかりやすい UX パターンを考案する必要があります。

ユーザー補助

ドラッグ&ドロップはアクセシビリティの高い操作ではありませんが、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)。