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を通して取得してください。