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

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

HTML5 ドラッグ&ドロップ API およびクリップボード イベントの一部である DataTransfer API についてお聞きになったことがあるかもしれません。ソース ターゲットと受信ターゲットの間でデータを転送するために使用できます。

対応ブラウザ

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

ソース

ドラッグ&ドロップとコピー&ペーストのインタラクションは、ページ内のインタラクションによく使用され、単純なテキストを A から B に転送します。しかし、多くの場合見落とされているのは、これらのインタラクションを使用してブラウザ ウィンドウの外部に移動できることです。

ブラウザに組み込まれたドラッグ&ドロップ操作とコピー&ペースト操作はどちらも、他のアプリケーション、ウェブ、その他のアプリケーションと通信でき、オリジンには関連付けられません。この API は、データの転送先に応じて異なる動作をする複数のデータエントリをサポートしています。ウェブ アプリケーションは、受信イベントをリッスンするときに転送されたデータを送受信できます。

この機能により、デスクトップのウェブ アプリケーションでの共有と相互運用性に対する考え方が変わる可能性があります。アプリケーション間でのデータ転送で、密結合な統合に依存する必要がなくなりました。代わりに、ユーザーが任意の場所にデータを転送する完全な制御権を付与できます。

DataTransfer API で可能な操作の例。(動画に音声は含まれません)。

データの移行

まず、ドラッグ ドロップまたはコピー&ペーストを実装する必要があります。以下の例はドラッグ&ドロップ操作を示していますが、コピー&ペーストのプロセスも同様です。Drag and Drop 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: HTML ペイロードを contentEditable 要素と、Google ドキュメント、Microsoft Word などのリッチテキスト(WYSIWYG)エディタでレンダリングします。
  • 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(リンク データ)標準は、この目的に適しています。軽量で、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 ErtelUnsplash より)。