この記事では、JavaScriptのClipborad APIを使って、クリップボードにテキストをコピーする方法と、その周辺についてまとめています。
主なターゲットとして
- ボタンを押したらテキストをコピーできる機能を作りたい人
-
document.execCommand()
(後述)を使っている人
を想定しています。
「ボタンを押す→テキストをコピー」
Atcoderなどの競プロサイトや、Qiitaなどの技術記事投稿サイトでよく見られる機能です。
Copy ボタンを押すことで、入力例やコードをクリップボードにコピーしています。
これを実現する方法はいくつか存在しています。
Atcoder
AtcoderではCommandline APIに用意されているcopy()
関数を利用しています。
if (document.queryCommandSupported('copy') && (typeof PRINT === 'undefined')) {
...
}
$('.btn-copy').click(function() {
window.getSelection().removeAllRanges();
try {
copy($('#'+$(this).data('target')).text()); // <- ここ!
...
} catch (err) {
console.log(err);
}
window.getSelection().removeAllRanges();
});
Commandline APIの関数が使用できるかを確かめるためにはdocument.queryCommandSupported(command)
を利用します。
const flag = document.queryCommandSupported("SelectAll");
if(flag) {
copy('Hello world!');
}
しかしこの機能は非推奨です。
非推奨: この機能は非推奨になりました。まだ対応しているブラウザーがあるかもしれませんが、すでに関連するウェブ標準から削除されているか、削除の手続き中であるか、互換性のためだけに残されている可能性があります。使用を避け、できれば既存のコードは更新してください。このページの下部にある互換性一覧表を見て判断してください。この機能は突然動作しなくなる可能性があることに注意してください。
記事執筆時点(2023年3月)において、全てのブラウザにおいて互換性がありますが、
この機能は、現在のどの仕様にも含まれていません。標準化される予定もありません。
当面この機能は使い続けられるだろうと思っていますが、これからクリップボードの操作を勉強しようと思っている人にはお勧めできません。
Qiita
Qiitaが使っている機能はAtcoderとは少し異なります。
HTML文書がdesignMode
に切り替わっているときに利用できるexecCommand
に用意されているcopy
コマンドを利用しています。
...
const e = document.createRange();
e.selectNode(t);
const n = window.getSelection();
n && (n.removeAllRanges(),
n.addRange(e),
document.execCommand("copy"), // <- ここ!
...
Range
インターフェースをクリップボードにコピーしています。
Range
はDocument.createRange()
で生成することができます。また、Selection.getRangeAt()
やdocument.caretRangeFromPoint()
で選択することもできます。
しかし、これもまた非推奨です。
非推奨: この機能は非推奨になりました。まだ対応しているブラウザーがあるかもしれませんが、すでに関連するウェブ標準から削除されているか、削除の手続き中であるか、互換性のためだけに残されている可能性があります。使用を避け、できれば既存のコードは更新してください。このページの下部にある互換性一覧表を見て判断してください。この機能は突然動作しなくなる可能性があることに注意してください。
代替: Clipboard API
document.execCommand()
のページには次のようなメモが書いてあります。
メモ: このメソッドは廃止されており、使用すべきではありません。特に、クリップボードを操作したい場合は Clipboard API の使用を検討してください。
これまで使われてきたCommandline APIやdocument.execCommand()
の代替として提示されているのがClipboard APIです。
この API は、 document.execCommand() を使用したクリップボードへのアクセスを置き換えるように設計されています。
Clipboard APIには4つのメソッドがあります。
- クリップボードへの書き込み系(
write / writeText
) - クリップボードの読み出し系(
read / readText
)
読み込み・読み出しでそれぞれ2つのメソッドが用意されていますが、writeText/readText
はtext/plain
専用であり、write/read
は一般のblob
に対して操作を行うことができます。
しかしClipboard APIにもいくつか問題点があります。
read/readText
はユーザーのクリップボードを覗くという性質上、Permissionが必要となります。
クリップボードからデータを読み込むには、権限 API の "clipboard-read" 権限を得る必要があります。
しかし現在存在する数多のAPIでは、Permissionの処理に一貫性がありません。そのため現在はPermission APIが開発中ですが、Firefoxなど複数のブラウザには互換性がない状態です。
今回はread/readText
を呼び出す際にPermmisionを得れば良いだけですが。
これとは別に、(たとえ間違ってでも)Permission Denyしてしまうと、Permissionを与え直すことが不便であるという問題も存在します。
パーミッションのプロンプトから位置を決して共有しないことを選択した場合(パーミッションを拒否した場合)、ブラウザーのメニューオプションを使用しないとパーミッションのプロンプトに戻ることはできません。
こればかりは避けては通れない課題ですね。
しかし、write/writeText
をするだけであれば構わないのでは?というのが正直なところ。
#clickbutton
を持つ要素がクリックされたときに、そのelement.textContent
をクリップボードにコピーするコードは以下のようになります。
...
<div id="clickbutton">
Here is the test content.
</div>
const clickClip = (e) => {
const btn_content = e.target.textContent;
navigator.clipboard.writeText(btn_content)
.then(() => {
console.log("Success!");
},
() => {
console.log("Ops, something went wrong...");
});
};
document.querySelector('#clickbutton').addEventListener('click', clickClip);
おわりに
最後までご覧いただきありがとうございます。
JavaScriptからクリップボードを使ってみたいと思って調べたところ、過渡期にあることがわかったので記事にしました。
普段はWeb開発をしています。
所属している研究室のWebサイトも作ったので是非みてください。
(近々、Clipboard APIを使ったデザインが登場する予定...楽しみ!)
それでは。