Subterranean Flower

Async Clipboard APIでJavaScriptからクリップボードを操作する

Author
古都こと
ふよんとえんよぷよぐやま!!!

JavaScriptからのクリップボードの操作は、長いあいだ開発者たちに切望されていました。かつてはFlashを使って実現するハックなどがありましたが、各ブラウザがexecCommandによるクリップボード操作を実装したことで、現在は落ち着きつつあります。

ただ、APIが綺麗に整備されているとは言いにくく、あまり現代的ではありません。そこで、スマートかつ現代的にクリップボードを扱うことができる、Async Clipboard APIの実装が進みつつあります。

Async Clipboard API

Async Clipboard APIはその名の通り非同期でクリップボードを扱うAPIです。Async Clipboard APIはPromiseを返すので、直感的に扱えるようになっています。

従来のAPIでも、execCommandを用いることによってクリップボードを操作することができました。しかし、バグが多くブラウザ間の整合性が取れているとは言い難く、実際に使おうとすると様々な問題に直面します。

また、execCommandによるクリップボード操作は同期的であるため、大きなデータを操作するとメインスレッドをブロックする可能性があり、結果としてUXを損ないます。

そもそもexecCommandは元々はDOM操作のための古いAPIであり、仕様の策定もあまり進んでいません。よってクリップボードの操作のためには、新しいAPIが必要だったのです。

Async Clipboard APIの仕様は https://w3c.github.io/clipboard-apis/ から見ることができます。

対応ブラウザ

Async Clipboard APIは現在のところChrome 66が対応予定となっているのみで、他のブラウザでは未実装です。ですが各ブラウザ共に実装予定ではあるので、気長に待ちましょう。

また、Chrome 66においても、実装されているのは一部機能のみです。

Async Clipboard APIの使い方

クリップボードにデータを書き込む

クリップボードにテキストを書き込むには、navigator.clipboardオブジェクトのwriteTextメソッドを呼び出します。

navigator.clipboard.writeText("このテキストをクリップボードに書き込む");

このメソッドは非同期的にクリップボードに書き込みPromiseを返すので、thenを使ったりawaitを使うなどして適切に処理してください。Promiseとasync/awaitについては「Promiseとasync/awaitでJavaScriptの非同期処理をシンプルに記述する」をご覧ください。

Chrome 66未実装)プレーンテキスト以外のデータを書き込むには、一度DataTransferオブジェクトを作成して、そこに書き込み、それからクリップボードに書き込みます。

const data = new DataTransfer();
data.items.add('こんにちは!', 'text/plain');
data.items.add('<strong>やっほー!</strong>', 'text/html');
navigator.clipboard.write(data);

クリップボードからデータを読み込む

クリップボードからテキストを読み込むには、readTextメソッドを呼び出します。

navigator.clipboard.readText()
    .then((data) => console.log(data))
    .catch((e) => console.error('failed'));

このメソッドもPromiseを返します。readTextメソッドを呼び出すと、パーミッションの要求ダイアログが表示されます。ユーザが許可すると成功しますが、拒否すると失敗します。パーミッションの確認・取得には後述のPermissions APIをご活用ください。

Chrome 66未実装)テキスト以外のデータを読み込むにはreadメソッドを使用します。このメソッドはPromise<DataTransfer>を返します。

navigator.clipboard.read().then((data) => {
    for (var i = 0; i < data.items.length; i++) {
        if (data.items[i].type == 'text/plain') {
            console.log(data.items[i].getAs('text/plain'))
        } else {
            console.error('テキストではありません…');
        }
    }
});

クリップボードの変化を受け取る

Chrome 66未実装)クリップボードの変化を受け取るにはnavigator.clipboardオブジェクトのclipboardchangeイベントに対するイベントリスナーを登録します。

navigator.clipboard.addEventListener('clipboardchange', (evt) => {
  navigator.clipboard.readText((data) => console.log(data));
});

この機能を使うにはパーミッションが必要です。readあるいはreadTextを呼び出して先にパーミッションを取得するか、後述するParmissions APIを使用してください。

パーミッションの取得

Permissions APIを用いてパーミッションを確認・要求することができます。Permissions APIについては、詳しくは「Permissions APIでブラウザからの各種機能へのアクセス権限を確認・要求する」をご覧ください。

Chrome 66未実装)Async Clipboard APIで利用できるパーミッションは以下の2つです:

  • clipboard-write
  • clipboard-read

基本的にパーミッションは明示的に取得する必要はありません。ですが例外として、クリップボードの変化を確認するclipboardchangeイベントを受け取る時は、clipboard-readパーミッションが必要になります。これはreadメソッドあるいはreadTextメソッドを呼び出して取得するか、Permissions APIを通して取得してください。