JavaScriptで任意のテキストをデバイスのクリップボードにコピーする方法。
ページのタイトルやURLなどをコピーするボタンの挙動の詳細が知りたかったので、JavaScriptで任意のテキストをデバイスのシステムクリップボードにコピーの実装方法のメモ。
JavaScriptでクリップボードにコピーする方法は以下の3つ。
- 方法1 : Clipboard APIを使う。 (SSL/TLS通信のサイトでのみ利用可能。IEは非対応。)
- 方法2 : IE独自仕様のwindow.clipboardDataを使う。(IEで対応する場合。)
- 方法3 : execCommand()メソッドを使う。(現在は廃止されているメソッド。非SSLサイトで対応する場合。)
クリップボードとは、短期間のデータ格納や転送のために使われるバッファであり、文書やアプリケーションの間で使われます。
これは、通常名前のない一時的なバッファとして実装され、貼り付けバッファと呼ばれることもあり、その環境で定義されたアプリケーションプログラムインターフェイスを使うことで、ほとんど全てのプログラムがアクセスすることができます。
Clipboard APIを使う方法
Clipboard APIのMozillaの公式ドキュメント。
Clipboard APIの要約
- Clipboard APIは、execCommand()メソッドの代替として設計されたもの。
- SSL(Secure Sockets Layer) / TLS(Transport Layer Security)のサイトでのみ利用可能。(非SSLサイトではclipboardオブジェクトにアクセスできない。)
- IEは非対応。
- ユーザーが許可している場合、システムクリップボードにアクセスして読み取りや書き込みを行う機能を提供する。
- Clipboard APIは、ユーザーエージェントの情報を保持しているNavigatorインターフェイスに読み取り専用のclipboardプロパティーを追加する。(clipboardオブジェクトを参照として返す。)
- clipboardオブジェクトのメソッドでシステムクリップボードにアクセスができる。
- Clipboard APIのすべてのメソッドは非同期で動作するので、クリップボードにアクセスできた場合にPromiseオブジェクトを返す。(コピーなど指定の処理が完了した段階で次の処理をしたい場合にはPromiseオブジェクトの持つメソッドを使用できる。)
- clipboardオブジェクトは、navigatorオブジェクトが持つclipboardプロパティーで参照できる。
- navigatorオブジェクトは、windowオブジェクトが持つnavigatorプロパティーで参照できる。(Webブラウザ上でwindowオブジェクトはグローバルオブジェクトなので、「window.」は省略できる。)
※ 非SSLの開発環境で動作したので、localhostなどのループバックアドレスなら非SSLでも利用できると思われるが、公式ドキュメントから情報が見つけられないので詳細不明。
[ clipboardオブジェクトが持つ主なメソッド ]
メソッド | 動作内容 |
read() | クリップボードから任意のデータ(画像など)を要求し、{jsxref(“Promise”)}}を返す。(デフォルトでは無効。) |
readText() | システムクリップボードからテキストを要求する。クリップボードにテキストが無ければ空文字列を返す。 |
write() | システムクリップボードに任意のデータを書き込む。(デフォルトでは無効。) |
writeText() | システムクリップボードにテキストを書き込む。 |
システムクリップボードへのアクセス権限
- ユーザーがクリップボードへのアクセスを許可しているかはPermissions APIによって確認できるが、現状Permissions APIの実装内容がブラウザによって違うようなので実用性は不明。
- もし「”clipboard-read”」や「”clipboard-write”」の権限が与えられていなければ、clipboardオブジェクトのメソッド呼び出しはできない。(現状、Permissions APIの実装内容がブラウザで異なるので詳細は未検証。)
※ Permissions APIについてのMozillaの公式ドキュメント。
JavaScriptでクリップボードにコピーの実装
「コピーする」ボタンをクリックしたら、任意のid属性(ここでは「txt-copy」)を付加しているHTML要素が内包するテキストをクリップボードにコピーする挙動をJavaScriptで実装。
1. HTMLのマークアップ
コピーするテキスト内容をユーザーが直接編集できるようにcontenteditable属性を付加しておく。
※ contenteditable属性がtrueの要素は、ブラウザ上で内容を編集できる。
※ contenteditable属性はユーザーの利便性のためなので、Clipboard APIでの必須設定ではない。
<p id="txt-copy" contenteditable="true">クリップボードにコピーしたいテキストの本文。</p>
<div id="btn-copy">コピーする</div>
2. Clipboard APIでクリップボードにコピーを実行
「クリップボードへのコピーを実行する関数」を作成して、「コピーする」ボタンのクリックイベントで実行する。
- ブラウザのJavaScriptサポート状況で処理を変えるので、各オブジェクトのプロパティーが存在するかを判定して分岐。
- コピーする要素をgetElementById()メソッドで取得したままだとElementオブジェクトであることを表す文字列がコピーされてしまうので、innerHTMLプロパティーを参照して、要素の子孫を文字列(DOMString)として取得する。(ターゲットとなる要素タグ自身も含める場合はouterHTMLプロパティーを参照。)
- ユーザーにコピー完了を伝えるメッセージなどを表示したい場合は、then()メソッド内で処理。
※ Promiseオブジェクトのthen()メソッドは、処理がresolve(解決)されたときに第1引数の関数、reject(拒否)されたときに第2引数の関数を実行する。(第2引数は省略可能。)
//クリップボードにコピーを実行する関数を作成
function ag2copy(){
//コピーしたいテキストを持つ要素を取得
let targetEle = document.getElementById('txt-copy');
//ブラウザの対応状況で分岐
if(navigator.clipboard){
//クリップボードにコピーを実行
navigator.clipboard.writeText(targetEle.innerHTML).then(function(){
//コピーに成功したときの処理...
console.log('copied.');
},function(){
//コピーに失敗したときの処理...
console.log('clipboard denied.');
});
}else if(window.clipboardData){
//IE用の処理...
}else if(document.execCommand){
//非SSLサイトと古いブラウザ用の処理...
}else{
//クリップボードにアクセスできなかった場合の処理
console.log('Can not copy. No permission and execCommand died.');
}
}
//コピーするボタンのクリックイベントに上記の関数を登録
const btnCopy = document.getElementById('btn-copy');
btnCopy.addEventListener('click', function(){
ag2copy();
});
document.getElementById(‘ID’) : 指定されたIDに一致する要素を表すElementオブジェクトを返す。無ければ「null」を返す。
要素.innerHTML : 指定した要素内のHTMLまたはXMLのマークアップを取得する。値を設定した場合は、要素のすべての子孫を削除して、htmlStringの文字列で与えられたHTMLを解析して構築されたノードに置き換える。
対象要素.addEventListener(‘イベントのタイプ’, ‘関数’, ‘イベント伝播順’) : 対象要素に指定のイベントでコールする関数を登録。第3引数(初期値 : false)でイベントの伝播する方向を指定できる。falseでDOM階層の下位から上位に伝播。
window.clipboardDataを使う方法
IEに対応する場合は、IE独自仕様のwindow.clipboardDataオブジェクトのメソッドを利用してクリップボードにコピーする。(IEでのみ利用できる。)
let targetEle = document.getElementById('txt-copy');
if(window.clipboardData){
//クリップボードにコピーを実行
let ieResult = window.clipboardData.setData("Text", targetEle.innerHTML);
//結果をコンソールに表示
console.log('setData : '+ieResult);
}
window.clipboardData.setData(フォーマット, データ) : 指定したフォーマットでのデータをクリップボードにセットする。フォーマットは、「”Text”」(テキスト形式のデータ)か「”URL”」(URL形式のデータ)を指定できる。成功した場合はtrueを返し、失敗した場合はfalseを返す。
execCommandメソッドを使う方法
execCommand()メソッドのMozillaの公式ドキュメント。
Clipboard APIを利用できない非SSLサイトや古いブラウザなどに対応する場合は、documentオブジェクトが持つexecCommand()メソッドを使用する。
※ execCommand()メソッドは、既に公式に廃止されているメソッド。
※ 後方互換のため現状はまだほとんどのブラウザがサポートしているが、いつサポートされなくなってもおかしくないので、一時的な対応として運用する。
execCommand()メソッドの要約
- execCommand()メソッドは、編集可能領域の内容にアクセスして引数に指定したコマンドを実行することができる。(フォームのテキスト編集可能な要素でのみ使用できる。)
- DOM上で現在「選択状態」になっている範囲に対して、指定したコマンドが実行される。
- 返値はBoolean。コマンドが対応していないか無効であればfalse、コマンドを実行できたらtrueを返す。
- ユーザーの操作で発火したイベントで実行された場合にのみexecCommand()メソッドはtrueを返して実行できる。(ユーザーの操作と関係無くsetTimeoutなどで実行させてもfalseが返り実行できない。)
※ Firefoxでは編集可能でないテキストのコピーもできたので、実際の実装状況は不明。
execCommand()メソッドでのコピーの実装
execCommand()メソッドの第1引数に「’copy’」を指定して実行することで、DOM上で現在「選択状態」になっている範囲がクリップボードにコピーされる。
※ execCommand()メソッドでコピーする場合、コピーしたいテキストが「選択状態」になっている必要がある。
コピー実行の手順
- 「input」要素を動的に生成。
- 生成した「input」要素の「value」属性の値にコピーしたいテキストを代入。
- 生成した「input」要素のstyleで「position:fixed;」を指定して表示領域の外に配置。(「display:none;」や「height:0px;」など、要素が不可視になるstyle設定だとテキストを選択状態にできなかったので注意。)
- 生成した「input」要素をDOMに挿入。
- 挿入した「input」要素が保持するテキストを「select()メソッド」で選択状態にする。
- 「execCommand(‘copy’)」を実行。
- 選択している範囲がクリップボードにコピーされる。
- 挿入した「input」要素を削除。
※ Fiferfoxでは「p」要素などのテキストでもdocumentオブジェクトのgetSelection()メソッドとcreateRange()メソッドを使ってコピーできたが、ChromeとSafariではコピーできなかったので、動的に生成した「input」要素にテキストを代入してからコピーする。
let targetEle = document.getElementById('txt-copy');
if(document.execCommand){
//「input」要素を生成して属性値とstyleを設定
let inputEle = document.createElement('input');
inputEle.setAttribute('type', 'text');
inputEle.setAttribute('value', targetEle.innerHTML);
inputEle.style.position = 'fixed';
inputEle.style.left = '-100%';
//「input」要素をbody内に挿入
document.body.appendChild(inputEle);
//テキストを選択状態にする
inputEle.select();
//コピーを実行
let execResult = document.execCommand('copy');
//「input」要素を削除
inputEle.parentNode.removeChild(inputEle);
//結果をコンソールに表示
console.log('execCommand : '+execResult);
}
document.createElement(‘タグ名’) : 指定したタグ名のHTML要素を生成する。
要素.setAttribute(‘属性名’, ‘値’) : 指定した要素に新しい属性値を追加、または既存の属性値を変更する。
親ノード.appendChild(‘ノード’) : 指定した親ノードの子ノードリストの末尾に引数で指定したノードを追加する。
document.execCommand(実行するコマンド名, aShowDefaultUI, aValueArgument) : 第1引数で指定したコマンドを実行する。第2引数「aShowDefaultUI」は既定のユーザインターフェースを表示するかどうかをBooleanで指定。(対応状況はブラウザによる。) 第3引数「aValueArgument」は追加の引数を必要とするコマンド名を指定している場合の引数。引数が不要な場合はnulldを指定。
ノード.parentNode : 指定したノードのDOMツリー内の親ノードを返す。
親ノード.removeChild(‘ノード’) : 指定した親ノードが保持する指定したノードをDOMから取り除く。(DOMの一部ではなくなるが、メモリ内には残る。変数などでノードへの参照を保持していなければ、普通はメモリからも自動的に削除される。)
https://memo.ag2works.tokyo/post-2504/
JavaScriptで任意のテキストをデバイスのクリップボードにコピーする方法。 | memo メモ [AG2WORKS]
<a href="https://memo.ag2works.tokyo/post-2504/" target="_blank" rel="noopener">JavaScriptで任意のテキストをデバイスのクリップボードにコピーする方法。 | memo メモ [AG2WORKS]</a>
この記事へのコメント
コメントの書き込みはまだありません。